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 <[email protected]> --- 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
