@samitouri / QOSamiQemu / commits / 9545c059f7

io: fix cleanup for websock I/O source data on cancellation

The websock code will create a GSource for tracking completion of the handshake process, passing a QIOTask which is freed by the callback when it completes, which means when a source is cancelled, nothing is free'ing the task. Switch to provide a data free callback to the GSource, which ensures the QIOTask is always freed even when the main event callback never fires. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/3114 Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>

Daniel P. Berrangé committed Jan 6, 2026 at 13:45 UTC 9545c059f77e3f814fcbaba83203572ea655c50e
1 file changed +35 -14
io/channel-websock.c
+35 -14
@@ -526,11 +526,32 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
526 return 1;
527 }
528
529 +typedef struct QIOChannelWebsockData {
530 + QIOTask *task;
531 +} QIOChannelWebsockData;
532 +
533 +static void qio_channel_websock_data_free(gpointer user_data)
534 +{
535 + QIOChannelWebsockData *data = user_data;
536 + /*
537 + * Usually 'task' will be NULL since the GSource
538 + * callback will either complete the task or pass
539 + * it on to a new GSource. We'll see a non-NULL
540 + * task here only if the GSource was released before
541 + * its callback triggers
542 + */
543 + if (data->task) {
544 + qio_task_free(data->task);
545 + }
546 + g_free(data);
547 +}
548 +
549 static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
550 GIOCondition condition,
551 gpointer user_data)
552 {
533 - QIOTask *task = user_data;
553 + QIOChannelWebsockData *data = user_data;
554 + QIOTask *task = data->task;
555 QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
556 qio_task_get_source(task));
557 Error *err = NULL;
@@ -545,7 +566,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
566 trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
567 qio_task_set_error(task, err);
568 qio_task_complete(task);
548 - qio_task_free(task);
569 wioc->hs_io_tag = 0;
570 return FALSE;
571 }
@@ -562,7 +582,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
582 trace_qio_channel_websock_handshake_complete(ioc);
583 qio_task_complete(task);
584 }
565 - qio_task_free(task);
585 wioc->hs_io_tag = 0;
586 return FALSE;
587 }
@@ -574,7 +593,8 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
593 GIOCondition condition,
594 gpointer user_data)
595 {
577 - QIOTask *task = user_data;
596 + QIOChannelWebsockData *data = user_data, *newdata = NULL;
597 + QIOTask *task = data->task;
598 QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
599 qio_task_get_source(task));
600 Error *err = NULL;
@@ -590,7 +610,6 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
610 trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
611 qio_task_set_error(task, err);
612 qio_task_complete(task);
593 - qio_task_free(task);
613 wioc->hs_io_tag = 0;
614 return FALSE;
615 }
@@ -603,12 +622,14 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
622 error_propagate(&wioc->io_err, err);
623
624 trace_qio_channel_websock_handshake_reply(ioc);
625 + newdata = g_new0(QIOChannelWebsockData, 1);
626 + newdata->task = g_steal_pointer(&data->task);
627 wioc->hs_io_tag = qio_channel_add_watch(
628 wioc->master,
629 G_IO_OUT,
630 qio_channel_websock_handshake_send,
610 - task,
611 - NULL);
631 + newdata,
632 + qio_channel_websock_data_free);
633 return FALSE;
634 }
635
@@ -904,12 +925,12 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
925 gpointer opaque,
926 GDestroyNotify destroy)
927 {
907 - QIOTask *task;
928 + QIOChannelWebsockData *data = g_new0(QIOChannelWebsockData, 1);
929
909 - task = qio_task_new(OBJECT(ioc),
910 - func,
911 - opaque,
912 - destroy);
930 + data->task = qio_task_new(OBJECT(ioc),
931 + func,
932 + opaque,
933 + destroy);
934
935 trace_qio_channel_websock_handshake_start(ioc);
936 trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
@@ -917,8 +938,8 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
938 ioc->master,
939 G_IO_IN,
940 qio_channel_websock_handshake_io,
920 - task,
921 - NULL);
941 + data,
942 + qio_channel_websock_data_free);
943 }
944
945