From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: den@openvz.org
Subject: [QEMU HCI-8.0 PATCH 4/7] hw/display/qxl: fix TOCTOU in cursor chunk data_size handling #VSTOR-144000
Date: Thu, 3 Sep 2026 22:25:46 +0200 [thread overview]
Message-ID: <20260903202549.2754937-5-den@openvz.org> (raw)
In-Reply-To: <20260903202549.2754937-1-den@openvz.org>
From: Marc-André Lureau <marcandre.lureau@redhat.com>
Snapshot chunk.data_size into a host-local variable before passing it to
qxl_phys2virt() for validation, and pass it through qxl_cursor() and
qxl_unpack_chunks() so that no subsequent code re-reads the field.
Without this, a racing vCPU can inflate data_size between the
qxl_phys2virt() validation and the memcpy in qxl_unpack_chunks(),
causing a source read past the validated region. In practice the read
stays within the guest's own VRAM mmap, so the impact is limited.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3757
Reported-by: Feifan Qian <bea1e@proton.me>
Signed-off-by: Marc-Andre Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Message-ID: <20260710134352.2313675-1-marcandre.lureau@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
(cherry picked from commit a3cc0069e151e5eb5db57bb4e86b00861d5b97ab)
---
hw/display/qxl-render.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index 1fe63b6f5ca..cf0f6849cd0 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -217,7 +217,8 @@ void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
}
static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
- QXLDataChunk *chunk, uint32_t group_id)
+ QXLDataChunk *chunk, uint32_t group_id,
+ uint32_t chunk_data_size)
{
uint32_t max_chunks = 32;
size_t offset = 0;
@@ -225,22 +226,21 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
QXLPHYSICAL next_chunk_phys = 0;
for (;;) {
- bytes = MIN(size - offset, chunk->data_size);
+ bytes = MIN(size - offset, chunk_data_size);
memcpy(dest + offset, chunk->data, bytes);
offset += bytes;
if (offset == size) {
return;
}
next_chunk_phys = chunk->next_chunk;
- /* fist time, only get the next chunk's data size */
chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,
sizeof(QXLDataChunk));
if (!chunk) {
return;
}
- /* second time, check data size and get data */
+ chunk_data_size = chunk->data_size;
chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,
- sizeof(QXLDataChunk) + chunk->data_size);
+ sizeof(QXLDataChunk) + chunk_data_size);
if (!chunk) {
return;
}
@@ -252,7 +252,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor,
- uint32_t group_id)
+ uint32_t group_id, uint32_t chunk_data_size)
{
QEMUCursor *c;
uint8_t *and_mask, *xor_mask;
@@ -272,11 +272,11 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor,
case SPICE_CURSOR_TYPE_MONO:
/* Assume that the full cursor is available in a single chunk. */
size = 2 * cursor_get_mono_bpl(c) * c->height;
- if (size != cursor->data_size || cursor->chunk.data_size < size) {
+ if (size != cursor->data_size || chunk_data_size < size) {
qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u"
" data_size %u chunk_size %u",
__func__, c->width, c->height,
- cursor->data_size, cursor->chunk.data_size);
+ cursor->data_size, chunk_data_size);
goto fail;
}
and_mask = cursor->chunk.data;
@@ -288,7 +288,8 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor,
break;
case SPICE_CURSOR_TYPE_ALPHA:
size = sizeof(uint32_t) * c->width * c->height;
- qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id);
+ qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id,
+ chunk_data_size);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/alpha");
}
@@ -325,19 +326,23 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
}
switch (cmd->type) {
case QXL_CURSOR_SET:
+ {
+ uint32_t chunk_data_size;
+
/* First read the QXLCursor to get QXLDataChunk::data_size ... */
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id,
sizeof(QXLCursor));
if (!cursor) {
return 1;
}
+ chunk_data_size = cursor->chunk.data_size;
/* Then read including the chunked data following QXLCursor. */
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id,
- sizeof(QXLCursor) + cursor->chunk.data_size);
+ sizeof(QXLCursor) + chunk_data_size);
if (!cursor) {
return 1;
}
- c = qxl_cursor(qxl, cursor, ext->group_id);
+ c = qxl_cursor(qxl, cursor, ext->group_id, chunk_data_size);
if (c == NULL) {
c = cursor_builtin_left_ptr();
}
@@ -351,6 +356,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
qemu_mutex_unlock(&qxl->ssd.lock);
qemu_bh_schedule(qxl->ssd.cursor_bh);
break;
+ }
case QXL_CURSOR_MOVE:
qemu_mutex_lock(&qxl->ssd.lock);
qxl->ssd.mouse_x = cmd->u.position.x;
--
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 ` Denis V. Lunev [this message]
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 ` [QEMU HCI-8.0 PATCH 6/7] hw/display/qxl: unregister vm_change_state handler and BHs #VSTOR-144000 Denis V. Lunev
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-5-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.