From abc177e885c379d66f38db4436a977953362e718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 25 Apr 2026 01:00:51 +0400 Subject: [PATCH] net/colo-compare: guard finalize against uninitialized state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit colo_compare_finalize() assumes the object was fully set up by colo_compare_complete(), but a bare object_new() followed by object_unref() skips the complete callback entirely. This causes two crashes: - qemu_mutex_destroy on the static event_mtx which was never initialized (colo_compare_active is false) - qemu_bh_delete(NULL) and iothread dereference when s->iothread is NULL Guard the event_mtx teardown with colo_compare_active, and the iothread-dependent cleanup with an s->iothread NULL check. Fixes: 45942b79b9f8 ("net/colo-compare.c: Check that colo-compare is active") Cc: peterx@redhat.com Acked-by: Peter Xu Signed-off-by: Marc-André Lureau --- net/colo-compare.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/net/colo-compare.c b/net/colo-compare.c index abc1326b70..823b8aa323 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1416,7 +1416,7 @@ static void colo_compare_finalize(Object *obj) break; } } - if (QTAILQ_EMPTY(&net_compares)) { + if (colo_compare_active && QTAILQ_EMPTY(&net_compares)) { colo_compare_active = false; qemu_mutex_destroy(&event_mtx); qemu_cond_destroy(&event_complete_cond); @@ -1431,30 +1431,29 @@ static void colo_compare_finalize(Object *obj) } colo_compare_timer_del(s); + g_clear_pointer(&s->event_bh, qemu_bh_delete); - qemu_bh_delete(s->event_bh); + if (s->iothread) { + AioContext *ctx = iothread_get_aio_context(s->iothread); - AioContext *ctx = iothread_get_aio_context(s->iothread); - AIO_WAIT_WHILE(ctx, !s->out_sendco.done); - if (s->notify_dev) { - AIO_WAIT_WHILE(ctx, !s->notify_sendco.done); + AIO_WAIT_WHILE(ctx, !s->out_sendco.done); + if (s->notify_dev) { + AIO_WAIT_WHILE(ctx, !s->notify_sendco.done); + } + + /* Release all unhandled packets after compare thread exited */ + g_queue_foreach(&s->conn_list, colo_flush_packets, s); + AIO_WAIT_WHILE(NULL, !s->out_sendco.done); + + object_unref(OBJECT(s->iothread)); } - /* Release all unhandled packets after compare thead exited */ - g_queue_foreach(&s->conn_list, colo_flush_packets, s); - AIO_WAIT_WHILE(NULL, !s->out_sendco.done); - g_queue_clear(&s->conn_list); g_queue_clear(&s->out_sendco.send_list); if (s->notify_dev) { g_queue_clear(&s->notify_sendco.send_list); } - - if (s->connection_track_table) { - g_hash_table_destroy(s->connection_track_table); - } - - object_unref(OBJECT(s->iothread)); + g_clear_pointer(&s->connection_track_table, g_hash_table_destroy); g_free(s->pri_indev); g_free(s->sec_indev);