From: Marc-André Lureau <[email protected]>
A malicious or buggy vhost-user-gpu backend can send messages with
undersized payloads, leading to out-of-bounds reads when the handler
accesses struct fields beyond the allocated buffer. However,
vhost-user-gpu is considered trusted by QEMU by design (it has access to
shared memory etc).
Add a centralized minimum payload size check in vhost_user_gpu_chr_read()
that rejects messages before dispatch, and a per-pixel bounds check in
the VHOST_USER_GPU_UPDATE handler to ensure the variable-length data
covers the declared width x height.
Fixes: 267f66465 ("hw/display: add vhost-user-vga & gpu-pci")
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3866
Reported-by: Feifan Qian <[email protected]>
Reviewed-by: Thomas Huth <[email protected]>
Signed-off-by: Marc-André Lureau <[email protected]>
(cherry picked from commit 1c1232c2ee0798e5f8446200005f045a153a5648)
Signed-off-by: Michael Tokarev <[email protected]>
diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c
index 2aed6243f6c..ff34d0b897d 100644
--- a/hw/display/vhost-user-gpu.c
+++ b/hw/display/vhost-user-gpu.c
@@ -118,6 +118,31 @@ static VhostUserGpuMsg m __attribute__ ((unused));
static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked);
+static size_t
+vhost_user_gpu_min_payload_size(VhostUserGpuRequest request)
+{
+ switch (request) {
+ case VHOST_USER_GPU_CURSOR_POS:
+ case VHOST_USER_GPU_CURSOR_POS_HIDE:
+ return sizeof(VhostUserGpuCursorPos);
+ case VHOST_USER_GPU_CURSOR_UPDATE:
+ return sizeof(VhostUserGpuCursorUpdate);
+ case VHOST_USER_GPU_GET_EDID:
+ return sizeof(VhostUserGpuEdidRequest);
+ case VHOST_USER_GPU_SCANOUT:
+ return sizeof(VhostUserGpuScanout);
+ case VHOST_USER_GPU_DMABUF_SCANOUT:
+ return sizeof(VhostUserGpuDMABUFScanout);
+ case VHOST_USER_GPU_DMABUF_SCANOUT2:
+ return sizeof(VhostUserGpuDMABUFScanout2);
+ case VHOST_USER_GPU_DMABUF_UPDATE:
+ case VHOST_USER_GPU_UPDATE:
+ return sizeof(VhostUserGpuUpdate);
+ default:
+ return 0;
+ }
+}
+
static void
vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg)
{
@@ -319,6 +344,14 @@ vhost_user_gpu_handle_display(VhostUserGPU *g,
VhostUserGpuMsg *msg)
if (m->scanout_id >= g->parent_obj.conf.max_outputs) {
break;
}
+
+ if ((uint64_t)m->width * m->height >
+ (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) {
+ error_report("vhost-user-gpu: update payload too small"
+ " for %ux%u", m->width, m->height);
+ break;
+ }
+
s = &g->parent_obj.scanout[m->scanout_id];
con = s->con;
pixman_image_t *image =
@@ -393,6 +426,11 @@ vhost_user_gpu_chr_read(void *opaque)
msg->flags = flags;
msg->size = size;
+ if (size < vhost_user_gpu_min_payload_size(request)) {
+ error_report("vhost-user-gpu: message %d payload too small", request);
+ goto end;
+ }
+
if (request == VHOST_USER_GPU_CURSOR_UPDATE ||
request == VHOST_USER_GPU_CURSOR_POS ||
request == VHOST_USER_GPU_CURSOR_POS_HIDE) {
--
2.47.3