From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: den@openvz.org
Subject: [QEMU HCI-8.0 PATCH 6/7] hw/display/qxl: unregister vm_change_state handler and BHs #VSTOR-144000
Date: Thu, 3 Sep 2026 22:25:48 +0200 [thread overview]
Message-ID: <20260903202549.2754937-7-den@openvz.org> (raw)
In-Reply-To: <20260903202549.2754937-1-den@openvz.org>
From: Haotian Jiang <sundayjiang@tencent.com>
qxl_realize_common() registers a vm_change_state handler via
qemu_add_vm_change_state_handler() and creates three bottom halves
(update_irq, update_area_bh, cursor_bh), but none are ever cleaned up.
The return value of qemu_add_vm_change_state_handler() is discarded, so
the handler is never removed from the global list, and there is no
PCIDeviceClass.exit callback to delete the BHs.
When a secondary QXL device (hotpluggable by default) is hot-unplugged
via device_del, the PCIQXLDevice memory is freed but the vm_state
handler and BH entries remain with dangling opaque pointers. On the
next VM state change (stop/cont/migrate) or BH dispatch, the callback
dereferences freed memory, causing a use-after-free.
Fix this by storing the VMChangeStateEntry returned by
qemu_add_vm_change_state_handler() and adding a qxl_exit() callback
that deletes the vm_state handler, all three BHs, and the
guest_surfaces.cmds allocation before the device memory is freed.
Fixes: a19cbfb34642 ("spice: add qxl device")
Fixes: CVE-2026-63322
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3607
Signed-off-by: Haotian Jiang <jianghaotian.sunday@gmail.com>
Cc: qemu-stable@nongnu.org
[ Marc-André - tweak commit message, add TODO ]
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20260720024855.3757499-1-jianghaotian.sunday@gmail.com>
(cherry picked from commit 4727cc883b7e81d2c30b9801af54482d65b84292)
---
hw/display/qxl.c | 16 +++++++++++++++-
hw/display/qxl.h | 1 +
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index a6eea18730e..994bfcaa522 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2225,7 +2225,8 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
}
#endif
- qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
+ qxl->vmstate_handler =
+ qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
qxl->update_irq = qemu_bh_new_guarded(qxl_update_irq_bh, qxl,
&DEVICE(qxl)->mem_reentrancy_guard);
@@ -2504,6 +2505,18 @@ static const Property qxl_properties[] = {
subsystem_vendor_id, PCI_VENDOR_ID_REDHAT_QUMRANET),
};
+static void qxl_exit(PCIDevice *dev)
+{
+ PCIQXLDevice *qxl = PCI_QXL(dev);
+
+ /* TODO: complete cleanup, error paths etc */
+ g_clear_pointer(&qxl->vmstate_handler, qemu_del_vm_change_state_handler);
+ g_clear_pointer(&qxl->update_irq, qemu_bh_delete);
+ g_clear_pointer(&qxl->update_area_bh, qemu_bh_delete);
+ g_clear_pointer(&qxl->ssd.cursor_bh, qemu_bh_delete);
+ g_clear_pointer(&qxl->guest_surfaces.cmds, g_free);
+}
+
static void qxl_pci_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -2511,6 +2524,7 @@ static void qxl_pci_class_init(ObjectClass *klass, const void *data)
k->vendor_id = REDHAT_PCI_VENDOR_ID;
k->device_id = QXL_DEVICE_ID_STABLE;
+ k->exit = qxl_exit;
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
device_class_set_legacy_reset(dc, qxl_reset_handler);
dc->vmsd = &qxl_vmstate;
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index 4afdbdd1ed8..a25d9865453 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -83,6 +83,7 @@ struct PCIQXLDevice {
/* thread signaling */
QEMUBH *update_irq;
+ VMChangeStateEntry *vmstate_handler;
/* ram pci bar */
QXLRam *ram;
--
2.53.0
next prev parent reply other threads:[~2026-09-03 20:26 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-03 20:25 [QEMU HCI-8.0 PATCH 0/7] qxl cursor use-after-free plus stability backports #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 1/7] hw/display/qxl: hold ssd.lock while replacing ssd.cursor #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 2/7] ui/cursor: make the cursor refcount atomic #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 3/7] hw/display/qxl: Fix mono cursor validation that can read past a cursor chunk #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 4/7] hw/display/qxl: fix TOCTOU in cursor chunk data_size handling #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 5/7] hw/display/qxl: validate monitors_config heads[] in phys2virt #VSTOR-144000 Denis V. Lunev
2026-09-03 20:25 ` Denis V. Lunev [this message]
2026-09-03 20:25 ` [QEMU HCI-8.0 PATCH 7/7] hw/display/qxl: validate primary surface stride against width #VSTOR-144000 Denis V. Lunev
2026-09-04 9:33 ` [QEMU HCI-8.0 PATCH 0/7] qxl cursor use-after-free plus stability backports #VSTOR-144000 Andrey Drobyshev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260903202549.2754937-7-den@openvz.org \
--to=den@openvz.org \
--cc=svt-core@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.