* [PATCH 1/4] virtio-gpu: make some 2d callbacks statics
2026-11-10 8:31 [PATCH 0/4] virtio-gpu: reject non-32bpp cursor resources Marc-André Lureau
@ 2026-11-10 8:31 ` Marc-André Lureau
2026-11-10 8:49 ` Daniel P. Berrangé
2026-11-11 13:13 ` Akihiko Odaki
2026-11-10 8:31 ` [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources Marc-André Lureau
` (2 subsequent siblings)
3 siblings, 2 replies; 13+ messages in thread
From: Marc-André Lureau @ 2026-11-10 8:31 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh,
Marc-André Lureau
Those functions are not shared with other units.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/display/virtio-gpu.c | 12 +++++++-----
include/hw/virtio/virtio-gpu.h | 4 ----
2 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 5d43ffb99184..6450f4734861 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -81,9 +81,10 @@ virtio_gpu_simple_resource_new_blob(uint32_t resource_id, uint64_t blob_size)
return res;
}
-void virtio_gpu_update_cursor_data(VirtIOGPU *g,
- struct virtio_gpu_scanout *s,
- uint32_t resource_id)
+static void
+virtio_gpu_update_cursor_data(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
{
struct virtio_gpu_simple_resource *res;
uint32_t pixels;
@@ -1178,8 +1179,9 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g,
virtio_gpu_cleanup_mapping(g, res);
}
-void virtio_gpu_simple_process_cmd(VirtIOGPU *g,
- struct virtio_gpu_ctrl_command *cmd)
+static void
+virtio_gpu_simple_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
{
VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 631dfc63a4d4..65e96b8fc4de 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -361,10 +361,6 @@ void virtio_gpu_cleanup_mapping(VirtIOGPU *g,
void virtio_gpu_process_cmdq(VirtIOGPU *g);
void virtio_gpu_device_realize(DeviceState *qdev, Error **errp);
void virtio_gpu_reset(VirtIODevice *vdev);
-void virtio_gpu_simple_process_cmd(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd);
-void virtio_gpu_update_cursor_data(VirtIOGPU *g,
- struct virtio_gpu_scanout *s,
- uint32_t resource_id);
bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id,
uint32_t width, uint32_t height,
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 1/4] virtio-gpu: make some 2d callbacks statics
2026-11-10 8:31 ` [PATCH 1/4] virtio-gpu: make some 2d callbacks statics Marc-André Lureau
@ 2026-11-10 8:49 ` Daniel P. Berrangé
2026-11-11 13:13 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 8:49 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh
On Tue, Nov 10, 2026 at 12:31:11PM +0400, Marc-André Lureau wrote:
> Those functions are not shared with other units.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> hw/display/virtio-gpu.c | 12 +++++++-----
> include/hw/virtio/virtio-gpu.h | 4 ----
> 2 files changed, 7 insertions(+), 9 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] virtio-gpu: make some 2d callbacks statics
2026-11-10 8:31 ` [PATCH 1/4] virtio-gpu: make some 2d callbacks statics Marc-André Lureau
2026-11-10 8:49 ` Daniel P. Berrangé
@ 2026-11-11 13:13 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Akihiko Odaki @ 2026-11-11 13:13 UTC (permalink / raw)
To: Marc-André Lureau, qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Dmitry Osipenko,
Stefano Garzarella, Gurchetan Singh
On 2026/11/10 17:31, Marc-André Lureau wrote:
> Those functions are not shared with other units.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Regards,
Akihiko Odaki
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources
2026-11-10 8:31 [PATCH 0/4] virtio-gpu: reject non-32bpp cursor resources Marc-André Lureau
2026-11-10 8:31 ` [PATCH 1/4] virtio-gpu: make some 2d callbacks statics Marc-André Lureau
@ 2026-11-10 8:31 ` Marc-André Lureau
2026-11-10 8:53 ` Daniel P. Berrangé
2026-11-10 8:31 ` [PATCH 3/4] virtio-gpu/rutabaga: " Marc-André Lureau
2026-11-10 8:31 ` [PATCH 4/4] contrib/vhost-user-gpu: " Marc-André Lureau
3 siblings, 1 reply; 13+ messages in thread
From: Marc-André Lureau @ 2026-11-10 8:31 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh,
Marc-André Lureau
Cursor pixel data is copied as 32bpp, but the resource format is never
checked. virgl_renderer_get_cursor_data() sizes its buffer for the
resource's own format, while virtio_gpu_gl_update_cursor_data() copies
width * height * 4 bytes from it: a 64x64 R8_UNORM resource gives a
4096 byte buffer and a 16384 byte read, so 12 KiB of host heap past the
end of the allocation is sent to a connected VNC client as cursor data.
Look up the resource format and refuse anything that is not 32bpp,
matching what the non-virgl paths accept. Querying the renderer covers
blob resources: a blob carries no format in the virtio command and QEMU
stores 0 for it. The virgl and virtio format enums have the same values
for the accepted formats.
Fixes: CVE-2026-122027
Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.")
Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/display/virtio-gpu-gl.c | 18 ++++++++++++++++++
include/hw/virtio/virtio-gpu-pixman.h | 13 +++++++++++++
2 files changed, 31 insertions(+)
diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
index 2b7a41c46643..c314631b05f3 100644
--- a/hw/display/virtio-gpu-gl.c
+++ b/hw/display/virtio-gpu-gl.c
@@ -31,6 +31,7 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
uint32_t resource_id)
{
VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
+ struct virgl_renderer_resource_info info = { 0 };
uint32_t width, height;
uint32_t pixels, *data;
@@ -38,6 +39,20 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
return;
}
+ virgl_renderer_force_ctx_0();
+ if (virgl_renderer_resource_get_info(resource_id, &info) != 0) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown cursor resource %u\n",
+ __func__, resource_id);
+ return;
+ }
+
+ if (!virtio_gpu_format_is_cursor_capable(info.virgl_format)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor resource %u has unsupported format %u\n",
+ __func__, resource_id, info.virgl_format);
+ return;
+ }
+
data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
if (!data) {
return;
@@ -45,6 +60,9 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
if (width != s->current_cursor->width ||
height != s->current_cursor->height) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor resource %u has unsupported dimension %ux%u\n",
+ __func__, resource_id, width, height);
free(data);
return;
}
diff --git a/include/hw/virtio/virtio-gpu-pixman.h b/include/hw/virtio/virtio-gpu-pixman.h
index 4dba782758dc..d6043d2ca1df 100644
--- a/include/hw/virtio/virtio-gpu-pixman.h
+++ b/include/hw/virtio/virtio-gpu-pixman.h
@@ -42,4 +42,17 @@ virtio_gpu_get_pixman_format(uint32_t virtio_gpu_format)
}
}
+/*
+ * Cursor data is copied as 32bpp; formats without a pixman mapping are
+ * rejected.
+ */
+static inline bool
+virtio_gpu_format_is_cursor_capable(uint32_t format)
+{
+ pixman_format_code_t pformat;
+
+ pformat = virtio_gpu_get_pixman_format(format);
+ return pformat && PIXMAN_FORMAT_BPP(pformat) == 32;
+}
+
#endif
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources
2026-11-10 8:31 ` [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources Marc-André Lureau
@ 2026-11-10 8:53 ` Daniel P. Berrangé
2026-11-11 13:19 ` Akihiko Odaki
0 siblings, 1 reply; 13+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 8:53 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh
On Tue, Nov 10, 2026 at 12:31:12PM +0400, Marc-André Lureau wrote:
> Cursor pixel data is copied as 32bpp, but the resource format is never
> checked. virgl_renderer_get_cursor_data() sizes its buffer for the
> resource's own format, while virtio_gpu_gl_update_cursor_data() copies
> width * height * 4 bytes from it: a 64x64 R8_UNORM resource gives a
> 4096 byte buffer and a 16384 byte read, so 12 KiB of host heap past the
> end of the allocation is sent to a connected VNC client as cursor data.
>
> Look up the resource format and refuse anything that is not 32bpp,
> matching what the non-virgl paths accept. Querying the renderer covers
> blob resources: a blob carries no format in the virtio command and QEMU
> stores 0 for it. The virgl and virtio format enums have the same values
> for the accepted formats.
>
> Fixes: CVE-2026-122027
> Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.")
> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> hw/display/virtio-gpu-gl.c | 18 ++++++++++++++++++
> include/hw/virtio/virtio-gpu-pixman.h | 13 +++++++++++++
> 2 files changed, 31 insertions(+)
>
> diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
> index 2b7a41c46643..c314631b05f3 100644
> --- a/hw/display/virtio-gpu-gl.c
> +++ b/hw/display/virtio-gpu-gl.c
> @@ -31,6 +31,7 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> uint32_t resource_id)
> {
> VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
> + struct virgl_renderer_resource_info info = { 0 };
> uint32_t width, height;
> uint32_t pixels, *data;
>
> @@ -38,6 +39,20 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
> return;
> }
>
> + virgl_renderer_force_ctx_0();
> + if (virgl_renderer_resource_get_info(resource_id, &info) != 0) {
> + qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown cursor resource %u\n",
> + __func__, resource_id);
> + return;
> + }
> +
> + if (!virtio_gpu_format_is_cursor_capable(info.virgl_format)) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: cursor resource %u has unsupported format %u\n",
> + __func__, resource_id, info.virgl_format);
> + return;
> + }
> +
> data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
> if (!data) {
> return;
> @@ -45,6 +60,9 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
>
> if (width != s->current_cursor->width ||
> height != s->current_cursor->height) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: cursor resource %u has unsupported dimension %ux%u\n",
> + __func__, resource_id, width, height);
> free(data);
> return;
> }
> diff --git a/include/hw/virtio/virtio-gpu-pixman.h b/include/hw/virtio/virtio-gpu-pixman.h
> index 4dba782758dc..d6043d2ca1df 100644
> --- a/include/hw/virtio/virtio-gpu-pixman.h
> +++ b/include/hw/virtio/virtio-gpu-pixman.h
> @@ -42,4 +42,17 @@ virtio_gpu_get_pixman_format(uint32_t virtio_gpu_format)
> }
> }
>
> +/*
> + * Cursor data is copied as 32bpp; formats without a pixman mapping are
> + * rejected.
> + */
> +static inline bool
> +virtio_gpu_format_is_cursor_capable(uint32_t format)
> +{
> + pixman_format_code_t pformat;
> +
> + pformat = virtio_gpu_get_pixman_format(format);
> + return pformat && PIXMAN_FORMAT_BPP(pformat) == 32;
Seems like 'pformat &&' is redundant since pixman_format_code_t is an
int (enum) and thus it is impossible for it to be 0, at the same time
as PIXMAN_FORMAT_BPP() reports 32.
Functionally it works though, so with/without it,
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources
2026-11-10 8:53 ` Daniel P. Berrangé
@ 2026-11-11 13:19 ` Akihiko Odaki
0 siblings, 0 replies; 13+ messages in thread
From: Akihiko Odaki @ 2026-11-11 13:19 UTC (permalink / raw)
To: Daniel P. Berrangé, Marc-André Lureau
Cc: qemu-devel, Michael S. Tsirkin, Alex Bennée, Dmitry Osipenko,
Stefano Garzarella, Gurchetan Singh
On 2026/11/10 17:53, Daniel P. Berrangé wrote:
> On Tue, Nov 10, 2026 at 12:31:12PM +0400, Marc-André Lureau wrote:
>> Cursor pixel data is copied as 32bpp, but the resource format is never
>> checked. virgl_renderer_get_cursor_data() sizes its buffer for the
>> resource's own format, while virtio_gpu_gl_update_cursor_data() copies
>> width * height * 4 bytes from it: a 64x64 R8_UNORM resource gives a
>> 4096 byte buffer and a 16384 byte read, so 12 KiB of host heap past the
>> end of the allocation is sent to a connected VNC client as cursor data.
>>
>> Look up the resource format and refuse anything that is not 32bpp,
>> matching what the non-virgl paths accept. Querying the renderer covers
>> blob resources: a blob carries no format in the virtio command and QEMU
>> stores 0 for it. The virgl and virtio format enums have the same values
>> for the accepted formats.
>>
>> Fixes: CVE-2026-122027
>> Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.")
>> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> ---
>> hw/display/virtio-gpu-gl.c | 18 ++++++++++++++++++
>> include/hw/virtio/virtio-gpu-pixman.h | 13 +++++++++++++
>> 2 files changed, 31 insertions(+)
>>
>> diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c
>> index 2b7a41c46643..c314631b05f3 100644
>> --- a/hw/display/virtio-gpu-gl.c
>> +++ b/hw/display/virtio-gpu-gl.c
>> @@ -31,6 +31,7 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
>> uint32_t resource_id)
>> {
>> VirtIOGPUGL *gl = VIRTIO_GPU_GL(g);
>> + struct virgl_renderer_resource_info info = { 0 };
>> uint32_t width, height;
>> uint32_t pixels, *data;
>>
>> @@ -38,6 +39,20 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
>> return;
>> }
>>
>> + virgl_renderer_force_ctx_0();
>> + if (virgl_renderer_resource_get_info(resource_id, &info) != 0) {
>> + qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown cursor resource %u\n",
>> + __func__, resource_id);
>> + return;
>> + }
>> +
>> + if (!virtio_gpu_format_is_cursor_capable(info.virgl_format)) {
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: cursor resource %u has unsupported format %u\n",
>> + __func__, resource_id, info.virgl_format);
>> + return;
>> + }
>> +
>> data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
>> if (!data) {
>> return;
>> @@ -45,6 +60,9 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g,
>>
>> if (width != s->current_cursor->width ||
>> height != s->current_cursor->height) {
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: cursor resource %u has unsupported dimension %ux%u\n",
>> + __func__, resource_id, width, height);
>> free(data);
>> return;
>> }
>> diff --git a/include/hw/virtio/virtio-gpu-pixman.h b/include/hw/virtio/virtio-gpu-pixman.h
>> index 4dba782758dc..d6043d2ca1df 100644
>> --- a/include/hw/virtio/virtio-gpu-pixman.h
>> +++ b/include/hw/virtio/virtio-gpu-pixman.h
>> @@ -42,4 +42,17 @@ virtio_gpu_get_pixman_format(uint32_t virtio_gpu_format)
>> }
>> }
>>
>> +/*
>> + * Cursor data is copied as 32bpp; formats without a pixman mapping are
>> + * rejected.
>> + */
>> +static inline bool
>> +virtio_gpu_format_is_cursor_capable(uint32_t format)
>> +{
>> + pixman_format_code_t pformat;
>> +
>> + pformat = virtio_gpu_get_pixman_format(format);
>> + return pformat && PIXMAN_FORMAT_BPP(pformat) == 32;
>
> Seems like 'pformat &&' is redundant since pixman_format_code_t is an
> int (enum) and thus it is impossible for it to be 0, at the same time
> as PIXMAN_FORMAT_BPP() reports 32.
>
> Functionally it works though, so with/without it,
>
> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
virtio_gpu_get_pixman_format() returns 0 for formats without a pixman
mapping though 0 is not part of pixman_format_code_t. It is confusing,
but the function is pre-existing, so:
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Regards,
Akihiko Odaki
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/4] virtio-gpu/rutabaga: reject non-32bpp cursor resources
2026-11-10 8:31 [PATCH 0/4] virtio-gpu: reject non-32bpp cursor resources Marc-André Lureau
2026-11-10 8:31 ` [PATCH 1/4] virtio-gpu: make some 2d callbacks statics Marc-André Lureau
2026-11-10 8:31 ` [PATCH 2/4] virtio-gpu/gl: reject non-32bpp cursor resources Marc-André Lureau
@ 2026-11-10 8:31 ` Marc-André Lureau
2026-11-10 8:54 ` Daniel P. Berrangé
2026-11-11 13:21 ` Akihiko Odaki
2026-11-10 8:31 ` [PATCH 4/4] contrib/vhost-user-gpu: " Marc-André Lureau
3 siblings, 2 replies; 13+ messages in thread
From: Marc-André Lureau @ 2026-11-10 8:31 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh,
Marc-André Lureau
The cursor buffer is always 64x64x4, but rutabaga does not check the
resource format before the transfer read. Gfxstream copies only as many
bytes as the resource format needs, so an R8_UNORM cursor updates the
first 4096 bytes of the 16384 byte buffer and leaves the rest untouched,
while formats with a larger pixel size do not fit the request and fail.
Refuse cursor resources that are not 32bpp, reusing
virtio_gpu_get_pixman_format() as the allowlist so the 32bpp formats the
other backends accept keep working. res->format is the value passed to
rutabaga_resource_create_3d(), so it matches what gfxstream stores. Blob
resources carry no format; they were already rejected by the dimension
check and stay rejected. Log the dimension mismatch and the transfer
failure as well.
Fixes: CVE-2026-122027
Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream")
Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/display/virtio-gpu-rutabaga.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c
index 1312a67d7011..6246aca894b0 100644
--- a/hw/display/virtio-gpu-rutabaga.c
+++ b/hw/display/virtio-gpu-rutabaga.c
@@ -44,8 +44,18 @@ virtio_gpu_rutabaga_update_cursor(VirtIOGPU *g, struct virtio_gpu_scanout *s,
return;
}
+ if (!virtio_gpu_format_is_cursor_capable(res->format)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor resource %u has unsupported format %u\n",
+ __func__, resource_id, res->format);
+ return;
+ }
+
if (res->width != s->current_cursor->width ||
res->height != s->current_cursor->height) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: cursor resource %u has unsupported dimension %ux%u\n",
+ __func__, resource_id, res->width, res->height);
return;
}
@@ -59,9 +69,12 @@ virtio_gpu_rutabaga_update_cursor(VirtIOGPU *g, struct virtio_gpu_scanout *s,
transfer_iovec.iov_base = s->current_cursor->data;
transfer_iovec.iov_len = res->width * res->height * 4;
- rutabaga_resource_transfer_read(vr->rutabaga, 0,
- resource_id, &transfer,
- &transfer_iovec);
+ if (rutabaga_resource_transfer_read(vr->rutabaga, 0, resource_id,
+ &transfer, &transfer_iovec) != 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: failed to read cursor resource %u\n",
+ __func__, resource_id);
+ }
}
static void
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 3/4] virtio-gpu/rutabaga: reject non-32bpp cursor resources
2026-11-10 8:31 ` [PATCH 3/4] virtio-gpu/rutabaga: " Marc-André Lureau
@ 2026-11-10 8:54 ` Daniel P. Berrangé
2026-11-11 13:21 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 8:54 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh
On Tue, Nov 10, 2026 at 12:31:13PM +0400, Marc-André Lureau wrote:
> The cursor buffer is always 64x64x4, but rutabaga does not check the
> resource format before the transfer read. Gfxstream copies only as many
> bytes as the resource format needs, so an R8_UNORM cursor updates the
> first 4096 bytes of the 16384 byte buffer and leaves the rest untouched,
> while formats with a larger pixel size do not fit the request and fail.
>
> Refuse cursor resources that are not 32bpp, reusing
> virtio_gpu_get_pixman_format() as the allowlist so the 32bpp formats the
> other backends accept keep working. res->format is the value passed to
> rutabaga_resource_create_3d(), so it matches what gfxstream stores. Blob
> resources carry no format; they were already rejected by the dimension
> check and stay rejected. Log the dimension mismatch and the transfer
> failure as well.
>
> Fixes: CVE-2026-122027
> Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream")
> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> hw/display/virtio-gpu-rutabaga.c | 19 ++++++++++++++++---
> 1 file changed, 16 insertions(+), 3 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 3/4] virtio-gpu/rutabaga: reject non-32bpp cursor resources
2026-11-10 8:31 ` [PATCH 3/4] virtio-gpu/rutabaga: " Marc-André Lureau
2026-11-10 8:54 ` Daniel P. Berrangé
@ 2026-11-11 13:21 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Akihiko Odaki @ 2026-11-11 13:21 UTC (permalink / raw)
To: Marc-André Lureau, qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Dmitry Osipenko,
Stefano Garzarella, Gurchetan Singh
On 2026/11/10 17:31, Marc-André Lureau wrote:
> The cursor buffer is always 64x64x4, but rutabaga does not check the
> resource format before the transfer read. Gfxstream copies only as many
> bytes as the resource format needs, so an R8_UNORM cursor updates the
> first 4096 bytes of the 16384 byte buffer and leaves the rest untouched,
> while formats with a larger pixel size do not fit the request and fail.
>
> Refuse cursor resources that are not 32bpp, reusing
> virtio_gpu_get_pixman_format() as the allowlist so the 32bpp formats the
> other backends accept keep working. res->format is the value passed to
> rutabaga_resource_create_3d(), so it matches what gfxstream stores. Blob
> resources carry no format; they were already rejected by the dimension
> check and stay rejected. Log the dimension mismatch and the transfer
> failure as well.
>
> Fixes: CVE-2026-122027
> Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream")
> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Regards,
Akihiko Odaki
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 4/4] contrib/vhost-user-gpu: reject non-32bpp cursor resources
2026-11-10 8:31 [PATCH 0/4] virtio-gpu: reject non-32bpp cursor resources Marc-André Lureau
` (2 preceding siblings ...)
2026-11-10 8:31 ` [PATCH 3/4] virtio-gpu/rutabaga: " Marc-André Lureau
@ 2026-11-10 8:31 ` Marc-André Lureau
2026-11-10 8:56 ` Daniel P. Berrangé
2026-11-11 13:29 ` Akihiko Odaki
3 siblings, 2 replies; 13+ messages in thread
From: Marc-André Lureau @ 2026-11-10 8:31 UTC (permalink / raw)
To: qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh,
Marc-André Lureau
Cursor pixel data is copied as 32bpp, but the resource format is never
checked. virgl_renderer_get_cursor_data() sizes its buffer for the
resource's own format, while vg_virgl_update_cursor_data() copies
64 * 64 * 4 bytes from it: a 64x64 R8_UNORM resource gives a 4096 byte
buffer, so 12 KiB past its end is read and handed to the display
backend as cursor pixels.
Look up the resource format and refuse anything that is not 32bpp,
matching the virtio-gpu backends.
While at it, let g_autofree release the buffer: the 64x64 check returned
before it was freed, letting a guest grow the backend's memory with
repeated UPDATE_CURSOR.
Fixes: CVE-2026-122027
Fixes: d52c454aadcd ("contrib: add vhost-user-gpu")
Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
contrib/vhost-user-gpu/vhost-user-gpu.c | 31 ++++++++++++++++++++-----------
contrib/vhost-user-gpu/virgl.c | 25 ++++++++++++++++++-------
contrib/vhost-user-gpu/virgl.h | 2 +-
3 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c
index f70b34c179f3..b34a1e013b8b 100644
--- a/contrib/vhost-user-gpu/vhost-user-gpu.c
+++ b/contrib/vhost-user-gpu/vhost-user-gpu.c
@@ -949,19 +949,22 @@ vg_handle_ctrl(VuDev *dev, int qidx)
}
}
-static void
+static bool
update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data)
{
struct virtio_gpu_simple_resource *res;
res = virtio_gpu_find_resource(g, resource_id);
- g_return_if_fail(res != NULL);
- g_return_if_fail(pixman_image_get_width(res->image) == 64);
- g_return_if_fail(pixman_image_get_height(res->image) == 64);
- g_return_if_fail(
- PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) == 32);
+ if (!res || !res->image ||
+ pixman_image_get_width(res->image) != 64 ||
+ pixman_image_get_height(res->image) != 64 ||
+ PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) != 32) {
+ g_debug("%s: invalid cursor resource %u", G_STRFUNC, resource_id);
+ return false;
+ }
memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint32_t));
+ return true;
}
static void
@@ -984,6 +987,7 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
break;
}
case VIRTIO_GPU_CMD_UPDATE_CURSOR: {
+ bool ok = true;
VhostUserGpuMsg msg = {
.request = VHOST_USER_GPU_CURSOR_UPDATE,
.size = sizeof(VhostUserGpuCursorUpdate),
@@ -998,12 +1002,17 @@ vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor)
}
};
g_debug("%s: update", G_STRFUNC);
- if (g->virgl) {
- vg_virgl_update_cursor_data(g, cursor->resource_id,
- msg.payload.cursor_update.data);
+ if (!cursor->resource_id) {
+ /* no resource: send a transparent cursor */
+ } else if (g->virgl) {
+ ok = vg_virgl_update_cursor_data(g, cursor->resource_id,
+ msg.payload.cursor_update.data);
} else {
- update_cursor_data_simple(g, cursor->resource_id,
- msg.payload.cursor_update.data);
+ ok = update_cursor_data_simple(g, cursor->resource_id,
+ msg.payload.cursor_update.data);
+ }
+ if (!ok) {
+ break;
}
vg_send_msg(g, &msg, -1);
break;
diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c
index 0ef4b9d8c903..8af5a456ee5b 100644
--- a/contrib/vhost-user-gpu/virgl.c
+++ b/contrib/vhost-user-gpu/virgl.c
@@ -14,24 +14,35 @@
#include "qemu/osdep.h"
#include <virglrenderer.h>
+#include "hw/virtio/virtio-gpu-pixman.h"
#include "virgl.h"
#include <epoxy/gl.h>
-void
+bool
vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
gpointer data)
{
- uint32_t width, height;
- uint32_t *cursor;
+ struct virgl_renderer_resource_info info = { 0 };
+ g_autofree uint32_t *cursor = NULL;
+ uint32_t width = 0, height = 0;
+
+ virgl_renderer_force_ctx_0();
+ if (virgl_renderer_resource_get_info(resource_id, &info) != 0 ||
+ !virtio_gpu_format_is_cursor_capable(info.virgl_format)) {
+ g_debug("%s: invalid cursor resource %u", G_STRFUNC, resource_id);
+ return false;
+ }
cursor = virgl_renderer_get_cursor_data(resource_id, &width, &height);
- g_return_if_fail(cursor != NULL);
- g_return_if_fail(width == 64);
- g_return_if_fail(height == 64);
+ if (!cursor || width != 64 || height != 64) {
+ g_debug("%s: failed to read cursor resource %u (%ux%u)",
+ G_STRFUNC, resource_id, width, height);
+ return false;
+ }
memcpy(data, cursor, 64 * 64 * sizeof(uint32_t));
- free(cursor);
+ return true;
}
static void
diff --git a/contrib/vhost-user-gpu/virgl.h b/contrib/vhost-user-gpu/virgl.h
index 17078783a50f..40caa766d3fe 100644
--- a/contrib/vhost-user-gpu/virgl.h
+++ b/contrib/vhost-user-gpu/virgl.h
@@ -20,7 +20,7 @@
bool vg_virgl_init(VuGpu *g);
uint32_t vg_virgl_get_num_capsets(void);
void vg_virgl_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd);
-void vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
+bool vg_virgl_update_cursor_data(VuGpu *g, uint32_t resource_id,
gpointer data);
#endif
--
2.56.0.116.g6de20f6092dc
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH 4/4] contrib/vhost-user-gpu: reject non-32bpp cursor resources
2026-11-10 8:31 ` [PATCH 4/4] contrib/vhost-user-gpu: " Marc-André Lureau
@ 2026-11-10 8:56 ` Daniel P. Berrangé
2026-11-11 13:29 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Daniel P. Berrangé @ 2026-11-10 8:56 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Michael S. Tsirkin, Alex Bennée, Akihiko Odaki,
Dmitry Osipenko, Stefano Garzarella, Gurchetan Singh
On Tue, Nov 10, 2026 at 12:31:14PM +0400, Marc-André Lureau wrote:
> Cursor pixel data is copied as 32bpp, but the resource format is never
> checked. virgl_renderer_get_cursor_data() sizes its buffer for the
> resource's own format, while vg_virgl_update_cursor_data() copies
> 64 * 64 * 4 bytes from it: a 64x64 R8_UNORM resource gives a 4096 byte
> buffer, so 12 KiB past its end is read and handed to the display
> backend as cursor pixels.
>
> Look up the resource format and refuse anything that is not 32bpp,
> matching the virtio-gpu backends.
>
> While at it, let g_autofree release the buffer: the 64x64 check returned
> before it was freed, letting a guest grow the backend's memory with
> repeated UPDATE_CURSOR.
>
> Fixes: CVE-2026-122027
> Fixes: d52c454aadcd ("contrib: add vhost-user-gpu")
> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
> contrib/vhost-user-gpu/vhost-user-gpu.c | 31 ++++++++++++++++++++-----------
> contrib/vhost-user-gpu/virgl.c | 25 ++++++++++++++++++-------
> contrib/vhost-user-gpu/virgl.h | 2 +-
> 3 files changed, 39 insertions(+), 19 deletions(-)
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH 4/4] contrib/vhost-user-gpu: reject non-32bpp cursor resources
2026-11-10 8:31 ` [PATCH 4/4] contrib/vhost-user-gpu: " Marc-André Lureau
2026-11-10 8:56 ` Daniel P. Berrangé
@ 2026-11-11 13:29 ` Akihiko Odaki
1 sibling, 0 replies; 13+ messages in thread
From: Akihiko Odaki @ 2026-11-11 13:29 UTC (permalink / raw)
To: Marc-André Lureau, qemu-devel
Cc: Michael S. Tsirkin, Alex Bennée, Dmitry Osipenko,
Stefano Garzarella, Gurchetan Singh
On 2026/11/10 17:31, Marc-André Lureau wrote:
> Cursor pixel data is copied as 32bpp, but the resource format is never
> checked. virgl_renderer_get_cursor_data() sizes its buffer for the
> resource's own format, while vg_virgl_update_cursor_data() copies
> 64 * 64 * 4 bytes from it: a 64x64 R8_UNORM resource gives a 4096 byte
> buffer, so 12 KiB past its end is read and handed to the display
> backend as cursor pixels.
>
> Look up the resource format and refuse anything that is not 32bpp,
> matching the virtio-gpu backends.
>
> While at it, let g_autofree release the buffer: the 64x64 check returned
> before it was freed, letting a guest grow the backend's memory with
> repeated UPDATE_CURSOR.
>
> Fixes: CVE-2026-122027
> Fixes: d52c454aadcd ("contrib: add vhost-user-gpu")
> Reported-by: Fernando Bortotti <https://gitlab.com/fernando.bortotti>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Regards,
Akihiko Odaki
^ permalink raw reply [flat|nested] 13+ messages in thread