diff options
| author | Daniel P. Berrangé <berrange@redhat.com> | 2026-01-06 13:45:10 +0000 |
|---|---|---|
| committer | Michael Tokarev <mjt@tls.msk.ru> | 2026-03-11 16:41:42 +0300 |
| commit | 580ead9cdfd53db864e7cfa08f31a9892afca847 (patch) | |
| tree | 738dd9b2bf5d95e67a424d4a282daa405724b46e | |
| parent | 5151109103b7910fd526c80ebcb41532e5da483a (diff) | |
| download | qemu-580ead9cdfd53db864e7cfa08f31a9892afca847.tar.gz qemu-580ead9cdfd53db864e7cfa08f31a9892afca847.zip | |
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>
(cherry picked from commit 9545c059f77e3f814fcbaba83203572ea655c50e)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
| -rw-r--r-- | io/channel-websock.c | 49 |
1 files changed, 35 insertions, 14 deletions
diff --git a/io/channel-websock.c b/io/channel-websock.c index 13975de4d8..c9d5861230 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -526,11 +526,32 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc, return 1; } +typedef struct QIOChannelWebsockData { + QIOTask *task; +} QIOChannelWebsockData; + +static void qio_channel_websock_data_free(gpointer user_data) +{ + QIOChannelWebsockData *data = user_data; + /* + * Usually 'task' will be NULL since the GSource + * callback will either complete the task or pass + * it on to a new GSource. We'll see a non-NULL + * task here only if the GSource was released before + * its callback triggers + */ + if (data->task) { + qio_task_free(data->task); + } + g_free(data); +} + static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, GIOCondition condition, gpointer user_data) { - QIOTask *task = user_data; + QIOChannelWebsockData *data = user_data; + QIOTask *task = data->task; QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( qio_task_get_source(task)); Error *err = NULL; @@ -545,7 +566,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err)); qio_task_set_error(task, err); qio_task_complete(task); - qio_task_free(task); wioc->hs_io_tag = 0; return FALSE; } @@ -562,7 +582,6 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc, trace_qio_channel_websock_handshake_complete(ioc); qio_task_complete(task); } - qio_task_free(task); wioc->hs_io_tag = 0; return FALSE; } @@ -574,7 +593,8 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, GIOCondition condition, gpointer user_data) { - QIOTask *task = user_data; + QIOChannelWebsockData *data = user_data, *newdata = NULL; + QIOTask *task = data->task; QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK( qio_task_get_source(task)); Error *err = NULL; @@ -590,7 +610,6 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err)); qio_task_set_error(task, err); qio_task_complete(task); - qio_task_free(task); wioc->hs_io_tag = 0; return FALSE; } @@ -603,12 +622,14 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc, error_propagate(&wioc->io_err, err); trace_qio_channel_websock_handshake_reply(ioc); + newdata = g_new0(QIOChannelWebsockData, 1); + newdata->task = g_steal_pointer(&data->task); wioc->hs_io_tag = qio_channel_add_watch( wioc->master, G_IO_OUT, qio_channel_websock_handshake_send, - task, - NULL); + newdata, + qio_channel_websock_data_free); return FALSE; } @@ -904,12 +925,12 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc, gpointer opaque, GDestroyNotify destroy) { - QIOTask *task; + QIOChannelWebsockData *data = g_new0(QIOChannelWebsockData, 1); - task = qio_task_new(OBJECT(ioc), - func, - opaque, - destroy); + data->task = qio_task_new(OBJECT(ioc), + func, + opaque, + destroy); trace_qio_channel_websock_handshake_start(ioc); trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN); @@ -917,8 +938,8 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc, ioc->master, G_IO_IN, qio_channel_websock_handshake_io, - task, - NULL); + data, + qio_channel_websock_data_free); } |
