From: Marc-AndrĂ© Lureau <[email protected]>

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: [email protected]
Acked-by: Peter Xu <[email protected]>
Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 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 abc1326b704..823b8aa323c 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);
 
-    /* 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);
+        object_unref(OBJECT(s->iothread));
+    }
 
     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);
-- 
2.54.0


Reply via email to