All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] qga: fix crash when a signal arrives during shutdown
@ 2026-10-31  1:07 Brad Smith
  2026-10-31 12:49 ` Roman Bogorodskiy
  0 siblings, 1 reply; 2+ messages in thread
From: Brad Smith @ 2026-10-31  1:07 UTC (permalink / raw)
  To: Michael Roth, Kostiantyn Kostiuk; +Cc: qemu-devel

qga: fix crash when a signal arrives during shutdown

quit_handler() stays installed for SIGINT and SIGTERM for the life
of the process and dereferences the global ga_state. cleanup_agent()
frees the agent state and clears ga_state, so a SIGINT or SIGTERM
arriving after cleanup makes quit_handler() dereference NULL in
ga_is_frozen() and the agent crashes with SIGSEGV.

This happens in practice when qemu-ga runs under something that
forwards signals. For example, running it under sudo and stopping it
with "pkill -f" signals both sudo and qemu-ga, so the agent receives
SIGTERM twice.

Reproduced on FreeBSD by sending SIGTERM after cleanup_agent() under
lldb.

Return early from quit_handler() when ga_state is NULL, and clear
ga_state as soon as the command state cleanup is done, before the
main loop and the state are freed, so a signal during the rest of
cleanup does not touch freed memory. ga_state cannot be cleared
earlier, as command cleanup callbacks such as
guest_fsfreeze_cleanup() still use it.

Signed-off-by: Brad Smith <brad@comstyle.com>
---
 qga/main.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/qga/main.c b/qga/main.c
index fd19c7037d..8cc9810572 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -167,6 +167,10 @@ init_dfl_pathnames(void)
 
 static void quit_handler(int sig)
 {
+    if (!ga_state) {
+        return;
+    }
+
     /* if we're frozen, don't exit unless we're absolutely forced to,
      * because it's basically impossible for graceful exit to complete
      * unless all log/pid files are on unfreezable filesystems. there's
@@ -1589,13 +1593,13 @@ static void cleanup_agent(GAState *s)
         ga_command_state_free(s->command_state);
         json_message_parser_destroy(&s->parser);
     }
+    ga_state = NULL;
     g_free(s->pstate_filepath);
     g_free(s->state_filepath_isfrozen);
     if (s->main_loop) {
         g_main_loop_unref(s->main_loop);
     }
     g_free(s);
-    ga_state = NULL;
 }
 
 static int run_agent_once(GAState *s)
-- 
2.56.0



^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] qga: fix crash when a signal arrives during shutdown
  2026-10-31  1:07 [PATCH] qga: fix crash when a signal arrives during shutdown Brad Smith
@ 2026-10-31 12:49 ` Roman Bogorodskiy
  0 siblings, 0 replies; 2+ messages in thread
From: Roman Bogorodskiy @ 2026-10-31 12:49 UTC (permalink / raw)
  To: Brad Smith; +Cc: Michael Roth, Kostiantyn Kostiuk, qemu-devel

  Brad Smith wrote:

> qga: fix crash when a signal arrives during shutdown

For some reason your commit message bodies duplicate the title.

> quit_handler() stays installed for SIGINT and SIGTERM for the life
> of the process and dereferences the global ga_state. cleanup_agent()
> frees the agent state and clears ga_state, so a SIGINT or SIGTERM
> arriving after cleanup makes quit_handler() dereference NULL in
> ga_is_frozen() and the agent crashes with SIGSEGV.
> 
> This happens in practice when qemu-ga runs under something that
> forwards signals. For example, running it under sudo and stopping it
> with "pkill -f" signals both sudo and qemu-ga, so the agent receives
> SIGTERM twice.
> 
> Reproduced on FreeBSD by sending SIGTERM after cleanup_agent() under
> lldb.
> 
> Return early from quit_handler() when ga_state is NULL, and clear
> ga_state as soon as the command state cleanup is done, before the
> main loop and the state are freed, so a signal during the rest of
> cleanup does not touch freed memory. ga_state cannot be cleared
> earlier, as command cleanup callbacks such as
> guest_fsfreeze_cleanup() still use it.
> 
> Signed-off-by: Brad Smith <brad@comstyle.com>
> ---
>  qga/main.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/qga/main.c b/qga/main.c
> index fd19c7037d..8cc9810572 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -167,6 +167,10 @@ init_dfl_pathnames(void)
>  
>  static void quit_handler(int sig)
>  {
> +    if (!ga_state) {
> +        return;
> +    }
> +
>      /* if we're frozen, don't exit unless we're absolutely forced to,
>       * because it's basically impossible for graceful exit to complete
>       * unless all log/pid files are on unfreezable filesystems. there's
> @@ -1589,13 +1593,13 @@ static void cleanup_agent(GAState *s)
>          ga_command_state_free(s->command_state);
>          json_message_parser_destroy(&s->parser);
>      }
> +    ga_state = NULL;
>      g_free(s->pstate_filepath);
>      g_free(s->state_filepath_isfrozen);
>      if (s->main_loop) {
>          g_main_loop_unref(s->main_loop);
>      }
>      g_free(s);
> -    ga_state = NULL;
>  }
>  
>  static int run_agent_once(GAState *s)
> -- 
> 2.56.0
> 

I was testing this change by sending SIGTERM in a loop and noticed that
it still crashes with SIGSEGV or SIGARBT from time to time. I was able
to fix that by ignoring signals in the clean up routines:

diff --git a/qga/main.c b/qga/main.c
index 8cc9810572..47686bca80 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -167,6 +167,12 @@ init_dfl_pathnames(void)
 
 static void quit_handler(int sig)
 {
+#ifndef _WIN32
+    struct sigaction action = {
+        .sa_handler = SIG_IGN,
+    };
+#endif
+
     if (!ga_state) {
         return;
     }
@@ -203,6 +209,13 @@ static void quit_handler(int sig)
         return;
 #endif
     }
+
+#ifndef _WIN32
+    sigemptyset(&action.sa_mask);
+    sigaction(SIGINT, &action, NULL);
+    sigaction(SIGTERM, &action, NULL);
+#endif
+
     g_debug("received signal num %d, quitting", sig);
 
     stop_agent(ga_state, true);
@@ -1587,6 +1600,18 @@ static void cleanup_agent(GAState *s)
     if (s->load_avg_pdh_query) {
         PdhCloseQuery(s->load_avg_pdh_query);
     }
+#else
+    struct sigaction action = {
+        .sa_handler = SIG_IGN,
+    };
+
+    sigemptyset(&action.sa_mask);
+    if (sigaction(SIGINT, &action, NULL) != 0) {
+        g_warning("error configuring signal handler: %s", strerror(errno));
+    }
+    if (sigaction(SIGTERM, &action, NULL) != 0) {
+        g_warning("error configuring signal handler: %s", strerror(errno));
+    }
 #endif
     if (s->command_state) {
         ga_command_state_cleanup_all(s->command_state);



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-31 12:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-31  1:07 [PATCH] qga: fix crash when a signal arrives during shutdown Brad Smith
2026-10-31 12:49 ` Roman Bogorodskiy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.