- kvm-io-move-websock-resource-release-to-close-method.patch [RHEL-120116] - kvm-io-fix-use-after-free-in-websocket-handshake-code.patch [RHEL-120116] - kvm-vfio-Disable-VFIO-migration-with-MultiFD-support.patch [RHEL-126573] - kvm-hw-arm-virt-Use-ACPI-PCI-hotplug-by-default-from-10..patch [RHEL-67323] - kvm-hw-arm-smmu-common-Check-SMMU-has-PCIe-Root-Complex-.patch [RHEL-73800] - kvm-hw-arm-virt-acpi-build-Re-arrange-SMMUv3-IORT-build.patch [RHEL-73800] - kvm-hw-arm-virt-acpi-build-Update-IORT-for-multiple-smmu.patch [RHEL-73800] - kvm-hw-arm-virt-Factor-out-common-SMMUV3-dt-bindings-cod.patch [RHEL-73800] - kvm-hw-arm-virt-Add-an-SMMU_IO_LEN-macro.patch [RHEL-73800] - kvm-hw-pci-Introduce-pci_setup_iommu_per_bus-for-per-bus.patch [RHEL-73800] - kvm-hw-arm-virt-Allow-user-creatable-SMMUv3-dev-instanti.patch [RHEL-73800] - kvm-qemu-options.hx-Document-the-arm-smmuv3-device.patch [RHEL-73800] - kvm-bios-tables-test-Allow-for-smmuv3-test-data.patch [RHEL-73800] - kvm-qtest-bios-tables-test-Add-tests-for-legacy-smmuv3-a.patch [RHEL-73800] - kvm-qtest-bios-tables-test-Update-tables-for-smmuv3-test.patch [RHEL-73800] - kvm-qtest-Do-not-run-bios-tables-test-on-aarch64.patch [] - Resolves: RHEL-120116 (CVE-2025-11234 qemu-kvm: VNC WebSocket handshake use-after-free [rhel-10.2]) - Resolves: RHEL-126573 (VFIO migration using multifd should be disabled by default) - Resolves: RHEL-67323 ([aarch64] Support ACPI based PCI hotplug on ARM) - Resolves: RHEL-73800 (NVIDIA:Grace-Hopper:Backport support for user-creatable nested SMMUv3 - RHEL 10.1)
190 lines
7.9 KiB
Diff
190 lines
7.9 KiB
Diff
From 728cf99416aaaae2cc0fca6ee88f28ccec33d697 Mon Sep 17 00:00:00 2001
|
|
From: Jon Maloy <jmaloy@redhat.com>
|
|
Date: Tue, 4 Nov 2025 17:28:47 -0500
|
|
Subject: [PATCH 02/16] io: fix use after free in websocket handshake code
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
RH-Author: Jon Maloy <jmaloy@redhat.com>
|
|
RH-MergeRequest: 419: io: move websock resource release to close method
|
|
RH-Jira: RHEL-120116
|
|
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
|
|
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
RH-Commit: [2/2] acdb5414387815a8b2f0a84a151990875947e855 (jmaloy/jmaloy-qemu-kvm-2)
|
|
|
|
JIRA: https://issues.redhat.com/browse/RHEL-120116
|
|
CVE: CVE-2025-11234
|
|
|
|
commit b7a1f2ca45c7865b9e98e02ae605a65fc9458ae9
|
|
Author: Daniel P. Berrangé <berrange@redhat.com>
|
|
Date: Tue Sep 30 12:03:15 2025 +0100
|
|
|
|
io: fix use after free in websocket handshake code
|
|
|
|
If the QIOChannelWebsock object is freed while it is waiting to
|
|
complete a handshake, a GSource is leaked. This can lead to the
|
|
callback firing later on and triggering a use-after-free in the
|
|
use of the channel. This was observed in the VNC server with the
|
|
following trace from valgrind:
|
|
|
|
==2523108== Invalid read of size 4
|
|
==2523108== at 0x4054A24: vnc_disconnect_start (vnc.c:1296)
|
|
==2523108== by 0x4054A24: vnc_client_error (vnc.c:1392)
|
|
==2523108== by 0x4068A09: vncws_handshake_done (vnc-ws.c:105)
|
|
==2523108== by 0x44863B4: qio_task_complete (task.c:197)
|
|
==2523108== by 0x448343D: qio_channel_websock_handshake_io (channel-websock.c:588)
|
|
==2523108== by 0x6EDB862: UnknownInlinedFun (gmain.c:3398)
|
|
==2523108== by 0x6EDB862: g_main_context_dispatch_unlocked.lto_priv.0 (gmain.c:4249)
|
|
==2523108== by 0x6EDBAE4: g_main_context_dispatch (gmain.c:4237)
|
|
==2523108== by 0x45EC79F: glib_pollfds_poll (main-loop.c:287)
|
|
==2523108== by 0x45EC79F: os_host_main_loop_wait (main-loop.c:310)
|
|
==2523108== by 0x45EC79F: main_loop_wait (main-loop.c:589)
|
|
==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835)
|
|
==2523108== by 0x454F300: qemu_default_main (main.c:37)
|
|
==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58)
|
|
==2523108== Address 0x57a6e0dc is 28 bytes inside a block of size 103,608 free'd
|
|
==2523108== at 0x5F2FE43: free (vg_replace_malloc.c:989)
|
|
==2523108== by 0x6EDC444: g_free (gmem.c:208)
|
|
==2523108== by 0x4053F23: vnc_update_client (vnc.c:1153)
|
|
==2523108== by 0x4053F23: vnc_refresh (vnc.c:3225)
|
|
==2523108== by 0x4042881: dpy_refresh (console.c:880)
|
|
==2523108== by 0x4042881: gui_update (console.c:90)
|
|
==2523108== by 0x45EFA1B: timerlist_run_timers.part.0 (qemu-timer.c:562)
|
|
==2523108== by 0x45EFC8F: timerlist_run_timers (qemu-timer.c:495)
|
|
==2523108== by 0x45EFC8F: qemu_clock_run_timers (qemu-timer.c:576)
|
|
==2523108== by 0x45EFC8F: qemu_clock_run_all_timers (qemu-timer.c:663)
|
|
==2523108== by 0x45EC765: main_loop_wait (main-loop.c:600)
|
|
==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835)
|
|
==2523108== by 0x454F300: qemu_default_main (main.c:37)
|
|
==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58)
|
|
==2523108== Block was alloc'd at
|
|
==2523108== at 0x5F343F3: calloc (vg_replace_malloc.c:1675)
|
|
==2523108== by 0x6EE2F81: g_malloc0 (gmem.c:133)
|
|
==2523108== by 0x4057DA3: vnc_connect (vnc.c:3245)
|
|
==2523108== by 0x448591B: qio_net_listener_channel_func (net-listener.c:54)
|
|
==2523108== by 0x6EDB862: UnknownInlinedFun (gmain.c:3398)
|
|
==2523108== by 0x6EDB862: g_main_context_dispatch_unlocked.lto_priv.0 (gmain.c:4249)
|
|
==2523108== by 0x6EDBAE4: g_main_context_dispatch (gmain.c:4237)
|
|
==2523108== by 0x45EC79F: glib_pollfds_poll (main-loop.c:287)
|
|
==2523108== by 0x45EC79F: os_host_main_loop_wait (main-loop.c:310)
|
|
==2523108== by 0x45EC79F: main_loop_wait (main-loop.c:589)
|
|
==2523108== by 0x423A56D: qemu_main_loop (runstate.c:835)
|
|
==2523108== by 0x454F300: qemu_default_main (main.c:37)
|
|
==2523108== by 0x73D6574: (below main) (libc_start_call_main.h:58)
|
|
==2523108==
|
|
|
|
The above can be reproduced by launching QEMU with
|
|
|
|
$ qemu-system-x86_64 -vnc localhost:0,websocket=5700
|
|
|
|
and then repeatedly running:
|
|
|
|
for i in {1..100}; do
|
|
(echo -n "GET / HTTP/1.1" && sleep 0.05) | nc -w 1 localhost 5700 &
|
|
done
|
|
|
|
CVE-2025-11234
|
|
Reported-by: Grant Millar | Cylo <rid@cylo.io>
|
|
Reviewed-by: Eric Blake <eblake@redhat.com>
|
|
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
|
|
|
|
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
|
|
---
|
|
include/io/channel-websock.h | 3 ++-
|
|
io/channel-websock.c | 22 ++++++++++++++++------
|
|
2 files changed, 18 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/include/io/channel-websock.h b/include/io/channel-websock.h
|
|
index e180827c57..6700cf8946 100644
|
|
--- a/include/io/channel-websock.h
|
|
+++ b/include/io/channel-websock.h
|
|
@@ -61,7 +61,8 @@ struct QIOChannelWebsock {
|
|
size_t payload_remain;
|
|
size_t pong_remain;
|
|
QIOChannelWebsockMask mask;
|
|
- guint io_tag;
|
|
+ guint hs_io_tag; /* tracking handshake task */
|
|
+ guint io_tag; /* tracking watch task */
|
|
Error *io_err;
|
|
gboolean io_eof;
|
|
uint8_t opcode;
|
|
diff --git a/io/channel-websock.c b/io/channel-websock.c
|
|
index a19b902ff9..ec5e09f9ab 100644
|
|
--- a/io/channel-websock.c
|
|
+++ b/io/channel-websock.c
|
|
@@ -545,6 +545,7 @@ 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);
|
|
+ wioc->hs_io_tag = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
@@ -560,6 +561,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
|
|
trace_qio_channel_websock_handshake_complete(ioc);
|
|
qio_task_complete(task);
|
|
}
|
|
+ wioc->hs_io_tag = 0;
|
|
return FALSE;
|
|
}
|
|
trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);
|
|
@@ -586,6 +588,7 @@ 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);
|
|
+ wioc->hs_io_tag = 0;
|
|
return FALSE;
|
|
}
|
|
if (ret == 0) {
|
|
@@ -597,7 +600,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
|
error_propagate(&wioc->io_err, err);
|
|
|
|
trace_qio_channel_websock_handshake_reply(ioc);
|
|
- qio_channel_add_watch(
|
|
+ wioc->hs_io_tag = qio_channel_add_watch(
|
|
wioc->master,
|
|
G_IO_OUT,
|
|
qio_channel_websock_handshake_send,
|
|
@@ -907,11 +910,12 @@ void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
|
|
|
|
trace_qio_channel_websock_handshake_start(ioc);
|
|
trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
|
|
- qio_channel_add_watch(ioc->master,
|
|
- G_IO_IN,
|
|
- qio_channel_websock_handshake_io,
|
|
- task,
|
|
- NULL);
|
|
+ ioc->hs_io_tag = qio_channel_add_watch(
|
|
+ ioc->master,
|
|
+ G_IO_IN,
|
|
+ qio_channel_websock_handshake_io,
|
|
+ task,
|
|
+ NULL);
|
|
}
|
|
|
|
|
|
@@ -922,6 +926,9 @@ static void qio_channel_websock_finalize(Object *obj)
|
|
buffer_free(&ioc->encinput);
|
|
buffer_free(&ioc->encoutput);
|
|
buffer_free(&ioc->rawinput);
|
|
+ if (ioc->hs_io_tag) {
|
|
+ g_source_remove(ioc->hs_io_tag);
|
|
+ }
|
|
if (ioc->io_tag) {
|
|
g_source_remove(ioc->io_tag);
|
|
}
|
|
@@ -1222,6 +1229,9 @@ static int qio_channel_websock_close(QIOChannel *ioc,
|
|
buffer_free(&wioc->encinput);
|
|
buffer_free(&wioc->encoutput);
|
|
buffer_free(&wioc->rawinput);
|
|
+ if (wioc->hs_io_tag) {
|
|
+ g_clear_handle_id(&wioc->hs_io_tag, g_source_remove);
|
|
+ }
|
|
if (wioc->io_tag) {
|
|
g_clear_handle_id(&wioc->io_tag, g_source_remove);
|
|
}
|
|
--
|
|
2.47.3
|
|
|