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