All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt()
@ 2022-11-28 13:48 Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [PATCH-for-7.2 1/5] hw/display/qxl: Have qxl_log_command Return early if no log_cmd handler Philippe Mathieu-Daudé
                   ` (6 more replies)
  0 siblings, 7 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Since v1:
- Addressed Marc-André review comments
- Moved overrun check in qxl_get_check_slot_offset()

memory_region_get_ram_ptr() returns a host pointer for a
MemoryRegion. Sometimes we do offset calculation using this
pointer without checking the underlying MemoryRegion size.

Wenxu Yin reported a buffer overrun in QXL. This series
aims to fix it. I haven't audited the other _get_ram_ptr()
uses (yet). Eventually we could rename it _get_ram_ptr_unsafe
and add a safer helper which checks for overrun.

Worth considering for 7.2?

Regards,

Phil.

Philippe Mathieu-Daudé (5):
  hw/display/qxl: Have qxl_log_command Return early if no log_cmd
    handler
  hw/display/qxl: Document qxl_phys2virt()
  hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion

 hw/display/qxl-logger.c | 22 +++++++++++++++++++---
 hw/display/qxl-render.c | 12 ++++++++----
 hw/display/qxl.c        | 37 ++++++++++++++++++++++++++++---------
 hw/display/qxl.h        | 23 ++++++++++++++++++++++-
 4 files changed, 77 insertions(+), 17 deletions(-)

-- 
2.38.1



^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH-for-7.2 1/5] hw/display/qxl: Have qxl_log_command Return early if no log_cmd handler
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
@ 2022-11-28 13:48 ` Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [PATCH-for-7.2 2/5] hw/display/qxl: Document qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Only 3 command types are logged: no need to call qxl_phys2virt()
for the other types. Using different cases will help to pass
different structure sizes to qxl_phys2virt() in a pair of commits.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/display/qxl-logger.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c
index 68bfa47568..1bcf803db6 100644
--- a/hw/display/qxl-logger.c
+++ b/hw/display/qxl-logger.c
@@ -247,6 +247,16 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
             qxl_name(qxl_type, ext->cmd.type),
             compat ? "(compat)" : "");
 
+    switch (ext->cmd.type) {
+    case QXL_CMD_DRAW:
+        break;
+    case QXL_CMD_SURFACE:
+        break;
+    case QXL_CMD_CURSOR:
+        break;
+    default:
+        goto out;
+    }
     data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
     if (!data) {
         return 1;
@@ -269,6 +279,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
         qxl_log_cmd_cursor(qxl, data, ext->group_id);
         break;
     }
+out:
     fprintf(stderr, "\n");
     return 0;
 }
-- 
2.38.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH-for-7.2 2/5] hw/display/qxl: Document qxl_phys2virt()
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [PATCH-for-7.2 1/5] hw/display/qxl: Have qxl_log_command Return early if no log_cmd handler Philippe Mathieu-Daudé
@ 2022-11-28 13:48 ` Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/display/qxl.h | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index e74de9579d..78b3a6c9ba 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -147,6 +147,25 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
 #define QXL_DEFAULT_REVISION (QXL_REVISION_STABLE_V12 + 1)
 
 /* qxl.c */
+/**
+ * qxl_phys2virt: Get a pointer within a PCI VRAM memory region.
+ *
+ * @qxl: QXL device
+ * @phys: physical offset of buffer within the VRAM
+ * @group_id: memory slot group
+ *
+ * Returns a host pointer to a buffer placed at offset @phys within the
+ * active slot @group_id of the PCI VGA RAM memory region associated with
+ * the @qxl device. If the slot is inactive, or the offset is out
+ * of the memory region, returns NULL.
+ *
+ * Use with care; by the time this function returns, the returned pointer is
+ * not protected by RCU anymore.  If the caller is not within an RCU critical
+ * section and does not hold the iothread lock, it must have other means of
+ * protecting the pointer, such as a reference to the region that includes
+ * the incoming ram_addr_t.
+ *
+ */
 void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
 void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
     G_GNUC_PRINTF(2, 3);
-- 
2.38.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [PATCH-for-7.2 1/5] hw/display/qxl: Have qxl_log_command Return early if no log_cmd handler Philippe Mathieu-Daudé
  2022-11-28 13:48 ` [PATCH-for-7.2 2/5] hw/display/qxl: Document qxl_phys2virt() Philippe Mathieu-Daudé
@ 2022-11-28 13:48 ` Philippe Mathieu-Daudé
  2022-11-28 13:53   ` Marc-André Lureau
  2022-11-28 15:08   ` Gerd Hoffmann
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144) Philippe Mathieu-Daudé
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Currently qxl_phys2virt() doesn't check for buffer overrun.
In order to do so in the next commit, pass the buffer size
as argument.

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
RFC: Please double-check qxl_render_update_area_unlocked()
---
 hw/display/qxl-logger.c | 11 ++++++++---
 hw/display/qxl-render.c | 12 ++++++++----
 hw/display/qxl.c        | 14 +++++++++-----
 hw/display/qxl.h        |  4 +++-
 4 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c
index 1bcf803db6..35c38f6252 100644
--- a/hw/display/qxl-logger.c
+++ b/hw/display/qxl-logger.c
@@ -106,7 +106,7 @@ static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
     QXLImage *image;
     QXLImageDescriptor *desc;
 
-    image = qxl_phys2virt(qxl, addr, group_id);
+    image = qxl_phys2virt(qxl, addr, group_id, sizeof(QXLImage));
     if (!image) {
         return 1;
     }
@@ -214,7 +214,8 @@ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
                 cmd->u.set.position.y,
                 cmd->u.set.visible ? "yes" : "no",
                 cmd->u.set.shape);
-        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
+        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id,
+                               sizeof(QXLCursor));
         if (!cursor) {
             return 1;
         }
@@ -236,6 +237,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
 {
     bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
     void *data;
+    size_t datasz;
     int ret;
 
     if (!qxl->cmdlog) {
@@ -249,15 +251,18 @@ int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
 
     switch (ext->cmd.type) {
     case QXL_CMD_DRAW:
+        datasz = compat ? sizeof(QXLCompatDrawable) : sizeof(QXLDrawable);
         break;
     case QXL_CMD_SURFACE:
+        datasz = sizeof(QXLSurfaceCmd);
         break;
     case QXL_CMD_CURSOR:
+        datasz = sizeof(QXLCursorCmd);
         break;
     default:
         goto out;
     }
-    data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+    data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, datasz);
     if (!data) {
         return 1;
     }
diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
index ca217004bf..0a4bfa8aa6 100644
--- a/hw/display/qxl-render.c
+++ b/hw/display/qxl-render.c
@@ -107,7 +107,9 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
         qxl->guest_primary.resized = 0;
         qxl->guest_primary.data = qxl_phys2virt(qxl,
                                                 qxl->guest_primary.surface.mem,
-                                                MEMSLOT_GROUP_GUEST);
+                                                MEMSLOT_GROUP_GUEST,
+                                                qxl->guest_primary.abs_stride
+                                                * height);
         if (!qxl->guest_primary.data) {
             goto end;
         }
@@ -228,7 +230,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
         if (offset == size) {
             return;
         }
-        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
+        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, bytes);
         if (!chunk) {
             return;
         }
@@ -295,7 +297,8 @@ fail:
 /* called from spice server thread context only */
 int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
 {
-    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id,
+                                      sizeof(QXLCursorCmd));
     QXLCursor *cursor;
     QEMUCursor *c;
 
@@ -314,7 +317,8 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
     }
     switch (cmd->type) {
     case QXL_CURSOR_SET:
-        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
+        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id,
+                               sizeof(QXLCursor));
         if (!cursor) {
             return 1;
         }
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 5b10f697f1..231d733250 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -274,7 +274,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
                                           QXL_IO_MONITORS_CONFIG_ASYNC));
     }
 
-    cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST);
+    cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST,
+                        sizeof(QXLMonitorsConfig));
     if (cfg != NULL && cfg->count == 1) {
         qxl->guest_primary.resized = 1;
         qxl->guest_head0_width  = cfg->heads[0].width;
@@ -459,7 +460,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
     switch (le32_to_cpu(ext->cmd.type)) {
     case QXL_CMD_SURFACE:
     {
-        QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+        QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id,
+                                           sizeof(QXLSurfaceCmd));
 
         if (!cmd) {
             return 1;
@@ -494,7 +496,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
     }
     case QXL_CMD_CURSOR:
     {
-        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
+        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id,
+                                          sizeof(QXLCursorCmd));
 
         if (!cmd) {
             return 1;
@@ -1456,7 +1459,8 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
 }
 
 /* can be also called from spice server thread context */
-void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
+void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id,
+                    size_t size)
 {
     uint64_t offset;
     uint32_t slot;
@@ -1964,7 +1968,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
         }
 
         cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
-                            MEMSLOT_GROUP_GUEST);
+                            MEMSLOT_GROUP_GUEST, sizeof(QXLSurfaceCmd));
         assert(cmd);
         assert(cmd->type == QXL_SURFACE_CMD_CREATE);
         qxl_dirty_one_surface(qxl, cmd->u.surface_create.data,
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index 78b3a6c9ba..bf03138ab4 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -153,6 +153,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
  * @qxl: QXL device
  * @phys: physical offset of buffer within the VRAM
  * @group_id: memory slot group
+ * @size: size of the buffer
  *
  * Returns a host pointer to a buffer placed at offset @phys within the
  * active slot @group_id of the PCI VGA RAM memory region associated with
@@ -166,7 +167,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
  * the incoming ram_addr_t.
  *
  */
-void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id,
+                    size_t size);
 void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
     G_GNUC_PRINTF(2, 3);
 
-- 
2.38.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (2 preceding siblings ...)
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt() Philippe Mathieu-Daudé
@ 2022-11-28 13:48 ` Philippe Mathieu-Daudé
  2022-11-28 15:16   ` Stefan Hajnoczi
  2022-11-28 13:48 ` [PATCH-for-8.0 5/5] hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion Philippe Mathieu-Daudé
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Have qxl_get_check_slot_offset() return false if the requested
buffer size does not fit within the slot memory region.

Similarly qxl_phys2virt() now returns NULL in such case, and
qxl_dirty_one_surface() aborts.

This avoids buffer overrun in the host pointer returned by
memory_region_get_ram_ptr().

Fixes: CVE-2022-4144 (out-of-bounds read)
Reported-by: Wenxu Yin (@awxylitol)
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/display/qxl.c | 22 ++++++++++++++++++----
 hw/display/qxl.h |  2 +-
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 231d733250..afa157d327 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
 
 /* can be also called from spice server thread context */
 static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
-                                      uint32_t *s, uint64_t *o)
+                                      uint32_t *s, uint64_t *o,
+                                      size_t size_requested)
 {
     uint64_t phys   = le64_to_cpu(pqxl);
     uint32_t slot   = (phys >> (64 -  8)) & 0xff;
     uint64_t offset = phys & 0xffffffffffff;
+    uint64_t size_available;
 
     if (slot >= NUM_MEMSLOTS) {
         qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
@@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
         return false;
     }
 
+    size_available = memory_region_size(qxl->guest_slots[slot].mr);
+    assert(qxl->guest_slots[slot].offset + offset < size_available);
+    size_available -= qxl->guest_slots[slot].offset + offset;
+    if (size_requested > size_available) {
+        qxl_set_guest_bug(qxl,
+                          "slot %d offset %"PRIu64" size %zu: "
+                          "overrun by %"PRIu64" bytes\n",
+                          slot, offset, size_requested,
+                          size_requested - size_available);
+        return false;
+    }
+
     *s = slot;
     *o = offset;
     return true;
@@ -1471,7 +1485,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id,
         offset = le64_to_cpu(pqxl) & 0xffffffffffff;
         return (void *)(intptr_t)offset;
     case MEMSLOT_GROUP_GUEST:
-        if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) {
+        if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size)) {
             return NULL;
         }
         ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr);
@@ -1937,9 +1951,9 @@ static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
     uint32_t slot;
     bool rc;
 
-    rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset);
-    assert(rc == true);
     size = (uint64_t)height * abs(stride);
+    rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size);
+    assert(rc == true);
     trace_qxl_surfaces_dirty(qxl->id, offset, size);
     qxl_set_dirty(qxl->guest_slots[slot].mr,
                   qxl->guest_slots[slot].offset + offset,
diff --git a/hw/display/qxl.h b/hw/display/qxl.h
index bf03138ab4..7894bd5134 100644
--- a/hw/display/qxl.h
+++ b/hw/display/qxl.h
@@ -157,7 +157,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
  *
  * Returns a host pointer to a buffer placed at offset @phys within the
  * active slot @group_id of the PCI VGA RAM memory region associated with
- * the @qxl device. If the slot is inactive, or the offset is out
+ * the @qxl device. If the slot is inactive, or the offset + size are out
  * of the memory region, returns NULL.
  *
  * Use with care; by the time this function returns, the returned pointer is
-- 
2.38.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH-for-8.0 5/5] hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (3 preceding siblings ...)
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144) Philippe Mathieu-Daudé
@ 2022-11-28 13:48 ` Philippe Mathieu-Daudé
  2022-11-28 13:51 ` [RFC PATCH-for-7.2 v2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
  2022-11-28 15:19 ` [RFC PATCH-for-7.2 " Stefan Hajnoczi
  6 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:48 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini, Philippe Mathieu-Daudé

Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
---
 hw/display/qxl.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index afa157d327..8468513f41 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1384,6 +1384,7 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
         qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region);
         return 1;
     }
+    assert(guest_end - pci_start <= memory_region_size(mr));
 
     virt_start = (intptr_t)memory_region_get_ram_ptr(mr);
     memslot.slot_id = slot_id;
-- 
2.38.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 v2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt()
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (4 preceding siblings ...)
  2022-11-28 13:48 ` [PATCH-for-8.0 5/5] hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion Philippe Mathieu-Daudé
@ 2022-11-28 13:51 ` Philippe Mathieu-Daudé
  2022-11-28 15:19 ` [RFC PATCH-for-7.2 " Stefan Hajnoczi
  6 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 13:51 UTC (permalink / raw)
  To: Marc-André Lureau, qemu-devel
  Cc: Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini

On 28/11/22 14:48, Philippe Mathieu-Daudé wrote:
> Since v1:
> - Addressed Marc-André review comments
> - Moved overrun check in qxl_get_check_slot_offset()
> 
> memory_region_get_ram_ptr() returns a host pointer for a
> MemoryRegion. Sometimes we do offset calculation using this
> pointer without checking the underlying MemoryRegion size.
> 
> Wenxu Yin reported a buffer overrun in QXL. This series
> aims to fix it. I haven't audited the other _get_ram_ptr()
> uses (yet). Eventually we could rename it _get_ram_ptr_unsafe
> and add a safer helper which checks for overrun.
> 
> Worth considering for 7.2?
> 
> Regards,
> 
> Phil.

This series is v2...

v1 was 
https://lore.kernel.org/qemu-devel/20221125154030.42108-1-philmd@linaro.org/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt() Philippe Mathieu-Daudé
@ 2022-11-28 13:53   ` Marc-André Lureau
  2022-11-28 15:08   ` Gerd Hoffmann
  1 sibling, 0 replies; 20+ messages in thread
From: Marc-André Lureau @ 2022-11-28 13:53 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: qemu-devel, Alexander Bulekov, Peter Maydell, Gerd Hoffmann,
	Mauro Matteo Cascella, Paolo Bonzini

[-- Attachment #1: Type: text/plain, Size: 8105 bytes --]

Hi

On Mon, Nov 28, 2022 at 5:48 PM Philippe Mathieu-Daudé <philmd@linaro.org>
wrote:

> Currently qxl_phys2virt() doesn't check for buffer overrun.
> In order to do so in the next commit, pass the buffer size
> as argument.
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
> RFC: Please double-check qxl_render_update_area_unlocked()
> ---
>  hw/display/qxl-logger.c | 11 ++++++++---
>  hw/display/qxl-render.c | 12 ++++++++----
>  hw/display/qxl.c        | 14 +++++++++-----
>  hw/display/qxl.h        |  4 +++-
>  4 files changed, 28 insertions(+), 13 deletions(-)
>
> diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c
> index 1bcf803db6..35c38f6252 100644
> --- a/hw/display/qxl-logger.c
> +++ b/hw/display/qxl-logger.c
> @@ -106,7 +106,7 @@ static int qxl_log_image(PCIQXLDevice *qxl,
> QXLPHYSICAL addr, int group_id)
>      QXLImage *image;
>      QXLImageDescriptor *desc;
>
> -    image = qxl_phys2virt(qxl, addr, group_id);
> +    image = qxl_phys2virt(qxl, addr, group_id, sizeof(QXLImage));
>      if (!image) {
>          return 1;
>      }
> @@ -214,7 +214,8 @@ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd
> *cmd, int group_id)
>                  cmd->u.set.position.y,
>                  cmd->u.set.visible ? "yes" : "no",
>                  cmd->u.set.shape);
> -        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
> +        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id,
> +                               sizeof(QXLCursor));
>          if (!cursor) {
>              return 1;
>          }
> @@ -236,6 +237,7 @@ int qxl_log_command(PCIQXLDevice *qxl, const char
> *ring, QXLCommandExt *ext)
>  {
>      bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
>      void *data;
> +    size_t datasz;
>      int ret;
>
>      if (!qxl->cmdlog) {
> @@ -249,15 +251,18 @@ int qxl_log_command(PCIQXLDevice *qxl, const char
> *ring, QXLCommandExt *ext)
>
>      switch (ext->cmd.type) {
>      case QXL_CMD_DRAW:
> +        datasz = compat ? sizeof(QXLCompatDrawable) : sizeof(QXLDrawable);
>          break;
>      case QXL_CMD_SURFACE:
> +        datasz = sizeof(QXLSurfaceCmd);
>          break;
>      case QXL_CMD_CURSOR:
> +        datasz = sizeof(QXLCursorCmd);
>          break;
>      default:
>          goto out;
>      }
> -    data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
> +    data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id, datasz);
>      if (!data) {
>          return 1;
>      }
> diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c
> index ca217004bf..0a4bfa8aa6 100644
> --- a/hw/display/qxl-render.c
> +++ b/hw/display/qxl-render.c
> @@ -107,7 +107,9 @@ static void
> qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
>          qxl->guest_primary.resized = 0;
>          qxl->guest_primary.data = qxl_phys2virt(qxl,
>
>  qxl->guest_primary.surface.mem,
> -                                                MEMSLOT_GROUP_GUEST);
> +                                                MEMSLOT_GROUP_GUEST,
> +
> qxl->guest_primary.abs_stride
>
+                                                * height);
>

Right, it looks like QXL device stride is in bytes.

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


>          if (!qxl->guest_primary.data) {
>              goto end;
>          }
> @@ -228,7 +230,7 @@ static void qxl_unpack_chunks(void *dest, size_t size,
> PCIQXLDevice *qxl,
>          if (offset == size) {
>              return;
>          }
> -        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
> +        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, bytes);
>          if (!chunk) {
>              return;
>          }
> @@ -295,7 +297,8 @@ fail:
>  /* called from spice server thread context only */
>  int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
>  {
> -    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
> +    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id,
> +                                      sizeof(QXLCursorCmd));
>      QXLCursor *cursor;
>      QEMUCursor *c;
>
> @@ -314,7 +317,8 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt
> *ext)
>      }
>      switch (cmd->type) {
>      case QXL_CURSOR_SET:
> -        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
> +        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id,
> +                               sizeof(QXLCursor));
>          if (!cursor) {
>              return 1;
>          }
> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
> index 5b10f697f1..231d733250 100644
> --- a/hw/display/qxl.c
> +++ b/hw/display/qxl.c
> @@ -274,7 +274,8 @@ static void
> qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
>                                            QXL_IO_MONITORS_CONFIG_ASYNC));
>      }
>
> -    cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config,
> MEMSLOT_GROUP_GUEST);
> +    cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config,
> MEMSLOT_GROUP_GUEST,
> +                        sizeof(QXLMonitorsConfig));
>      if (cfg != NULL && cfg->count == 1) {
>          qxl->guest_primary.resized = 1;
>          qxl->guest_head0_width  = cfg->heads[0].width;
> @@ -459,7 +460,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct
> QXLCommandExt *ext)
>      switch (le32_to_cpu(ext->cmd.type)) {
>      case QXL_CMD_SURFACE:
>      {
> -        QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data,
> ext->group_id);
> +        QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data,
> ext->group_id,
> +                                           sizeof(QXLSurfaceCmd));
>
>          if (!cmd) {
>              return 1;
> @@ -494,7 +496,8 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct
> QXLCommandExt *ext)
>      }
>      case QXL_CMD_CURSOR:
>      {
> -        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data,
> ext->group_id);
> +        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data,
> ext->group_id,
> +                                          sizeof(QXLCursorCmd));
>
>          if (!cmd) {
>              return 1;
> @@ -1456,7 +1459,8 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice
> *qxl, QXLPHYSICAL pqxl,
>  }
>
>  /* can be also called from spice server thread context */
> -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
> +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id,
> +                    size_t size)
>  {
>      uint64_t offset;
>      uint32_t slot;
> @@ -1964,7 +1968,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
>          }
>
>          cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
> -                            MEMSLOT_GROUP_GUEST);
> +                            MEMSLOT_GROUP_GUEST, sizeof(QXLSurfaceCmd));
>          assert(cmd);
>          assert(cmd->type == QXL_SURFACE_CMD_CREATE);
>          qxl_dirty_one_surface(qxl, cmd->u.surface_create.data,
> diff --git a/hw/display/qxl.h b/hw/display/qxl.h
> index 78b3a6c9ba..bf03138ab4 100644
> --- a/hw/display/qxl.h
> +++ b/hw/display/qxl.h
> @@ -153,6 +153,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
>   * @qxl: QXL device
>   * @phys: physical offset of buffer within the VRAM
>   * @group_id: memory slot group
> + * @size: size of the buffer
>   *
>   * Returns a host pointer to a buffer placed at offset @phys within the
>   * active slot @group_id of the PCI VGA RAM memory region associated with
> @@ -166,7 +167,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
>   * the incoming ram_addr_t.
>   *
>   */
> -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
> +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id,
> +                    size_t size);
>  void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
>      G_GNUC_PRINTF(2, 3);
>
> --
> 2.38.1
>
>

[-- Attachment #2: Type: text/html, Size: 10143 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt() Philippe Mathieu-Daudé
  2022-11-28 13:53   ` Marc-André Lureau
@ 2022-11-28 15:08   ` Gerd Hoffmann
  2022-11-28 15:41     ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2022-11-28 15:08 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

> @@ -228,7 +230,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
>          if (offset == size) {
>              return;
>          }
> -        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
> +        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, bytes);
>          if (!chunk) {
>              return;
>          }

Naa, its not that simple.  You get a QXLDataChunk passed in which
typically is verified *excluding* dynamically-sized chunk->data.

Also at least one code path (processing SPICE_CURSOR_TYPE_MONO in
qxl_cursor) goes access chunk.data[] without calling
qxl_unpack_chunks(), that needs additional verification too (or
switch it to call qxl_unpack_chunks, or just drop it because nobody
uses mono chrome cursors anyway).

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 13:48 ` [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144) Philippe Mathieu-Daudé
@ 2022-11-28 15:16   ` Stefan Hajnoczi
  2022-11-28 15:25     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Stefan Hajnoczi @ 2022-11-28 15:16 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Gerd Hoffmann, Mauro Matteo Cascella,
	Paolo Bonzini

On Mon, 28 Nov 2022 at 08:53, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> Have qxl_get_check_slot_offset() return false if the requested
> buffer size does not fit within the slot memory region.
>
> Similarly qxl_phys2virt() now returns NULL in such case, and
> qxl_dirty_one_surface() aborts.
>
> This avoids buffer overrun in the host pointer returned by
> memory_region_get_ram_ptr().
>
> Fixes: CVE-2022-4144 (out-of-bounds read)
> Reported-by: Wenxu Yin (@awxylitol)
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> ---
>  hw/display/qxl.c | 22 ++++++++++++++++++----
>  hw/display/qxl.h |  2 +-
>  2 files changed, 19 insertions(+), 5 deletions(-)
>
> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
> index 231d733250..afa157d327 100644
> --- a/hw/display/qxl.c
> +++ b/hw/display/qxl.c
> @@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
>
>  /* can be also called from spice server thread context */
>  static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
> -                                      uint32_t *s, uint64_t *o)
> +                                      uint32_t *s, uint64_t *o,
> +                                      size_t size_requested)
>  {
>      uint64_t phys   = le64_to_cpu(pqxl);
>      uint32_t slot   = (phys >> (64 -  8)) & 0xff;
>      uint64_t offset = phys & 0xffffffffffff;
> +    uint64_t size_available;
>
>      if (slot >= NUM_MEMSLOTS) {
>          qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
> @@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>          return false;
>      }
>
> +    size_available = memory_region_size(qxl->guest_slots[slot].mr);
> +    assert(qxl->guest_slots[slot].offset + offset < size_available);

Can this assertion be triggered by the guest (via an invalid pqxl
value)? I think the answer is no, but I don't know the the qxl code
well enough to be sure.

> +    size_available -= qxl->guest_slots[slot].offset + offset;
> +    if (size_requested > size_available) {
> +        qxl_set_guest_bug(qxl,
> +                          "slot %d offset %"PRIu64" size %zu: "
> +                          "overrun by %"PRIu64" bytes\n",
> +                          slot, offset, size_requested,
> +                          size_requested - size_available);
> +        return false;
> +    }
> +
>      *s = slot;
>      *o = offset;
>      return true;
> @@ -1471,7 +1485,7 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id,
>          offset = le64_to_cpu(pqxl) & 0xffffffffffff;
>          return (void *)(intptr_t)offset;
>      case MEMSLOT_GROUP_GUEST:
> -        if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) {
> +        if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size)) {
>              return NULL;
>          }
>          ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr);
> @@ -1937,9 +1951,9 @@ static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>      uint32_t slot;
>      bool rc;
>
> -    rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset);
> -    assert(rc == true);
>      size = (uint64_t)height * abs(stride);
> +    rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset, size);
> +    assert(rc == true);
>      trace_qxl_surfaces_dirty(qxl->id, offset, size);
>      qxl_set_dirty(qxl->guest_slots[slot].mr,
>                    qxl->guest_slots[slot].offset + offset,
> diff --git a/hw/display/qxl.h b/hw/display/qxl.h
> index bf03138ab4..7894bd5134 100644
> --- a/hw/display/qxl.h
> +++ b/hw/display/qxl.h
> @@ -157,7 +157,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL)
>   *
>   * Returns a host pointer to a buffer placed at offset @phys within the
>   * active slot @group_id of the PCI VGA RAM memory region associated with
> - * the @qxl device. If the slot is inactive, or the offset is out
> + * the @qxl device. If the slot is inactive, or the offset + size are out
>   * of the memory region, returns NULL.
>   *
>   * Use with care; by the time this function returns, the returned pointer is
> --
> 2.38.1
>
>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt()
  2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
                   ` (5 preceding siblings ...)
  2022-11-28 13:51 ` [RFC PATCH-for-7.2 v2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
@ 2022-11-28 15:19 ` Stefan Hajnoczi
  6 siblings, 0 replies; 20+ messages in thread
From: Stefan Hajnoczi @ 2022-11-28 15:19 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Gerd Hoffmann
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On Mon, 28 Nov 2022 at 08:50, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> Since v1:
> - Addressed Marc-André review comments
> - Moved overrun check in qxl_get_check_slot_offset()
>
> memory_region_get_ram_ptr() returns a host pointer for a
> MemoryRegion. Sometimes we do offset calculation using this
> pointer without checking the underlying MemoryRegion size.
>
> Wenxu Yin reported a buffer overrun in QXL. This series
> aims to fix it. I haven't audited the other _get_ram_ptr()
> uses (yet). Eventually we could rename it _get_ram_ptr_unsafe
> and add a safer helper which checks for overrun.
>
> Worth considering for 7.2?

Yes, please. If Gerd is happy I'll merge it.

Stefan

>
> Regards,
>
> Phil.
>
> Philippe Mathieu-Daudé (5):
>   hw/display/qxl: Have qxl_log_command Return early if no log_cmd
>     handler
>   hw/display/qxl: Document qxl_phys2virt()
>   hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
>   hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
>   hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion
>
>  hw/display/qxl-logger.c | 22 +++++++++++++++++++---
>  hw/display/qxl-render.c | 12 ++++++++----
>  hw/display/qxl.c        | 37 ++++++++++++++++++++++++++++---------
>  hw/display/qxl.h        | 23 ++++++++++++++++++++++-
>  4 files changed, 77 insertions(+), 17 deletions(-)
>
> --
> 2.38.1
>
>


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 15:16   ` Stefan Hajnoczi
@ 2022-11-28 15:25     ` Philippe Mathieu-Daudé
  2022-11-28 15:32       ` Stefan Hajnoczi
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 15:25 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Gerd Hoffmann, Mauro Matteo Cascella,
	Paolo Bonzini

On 28/11/22 16:16, Stefan Hajnoczi wrote:
> On Mon, 28 Nov 2022 at 08:53, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>>
>> Have qxl_get_check_slot_offset() return false if the requested
>> buffer size does not fit within the slot memory region.
>>
>> Similarly qxl_phys2virt() now returns NULL in such case, and
>> qxl_dirty_one_surface() aborts.
>>
>> This avoids buffer overrun in the host pointer returned by
>> memory_region_get_ram_ptr().
>>
>> Fixes: CVE-2022-4144 (out-of-bounds read)
>> Reported-by: Wenxu Yin (@awxylitol)
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>> ---
>>   hw/display/qxl.c | 22 ++++++++++++++++++----
>>   hw/display/qxl.h |  2 +-
>>   2 files changed, 19 insertions(+), 5 deletions(-)
>>
>> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
>> index 231d733250..afa157d327 100644
>> --- a/hw/display/qxl.c
>> +++ b/hw/display/qxl.c
>> @@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
>>
>>   /* can be also called from spice server thread context */
>>   static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>> -                                      uint32_t *s, uint64_t *o)
>> +                                      uint32_t *s, uint64_t *o,
>> +                                      size_t size_requested)
>>   {
>>       uint64_t phys   = le64_to_cpu(pqxl);
>>       uint32_t slot   = (phys >> (64 -  8)) & 0xff;
>>       uint64_t offset = phys & 0xffffffffffff;
>> +    uint64_t size_available;
>>
>>       if (slot >= NUM_MEMSLOTS) {
>>           qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
>> @@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>>           return false;
>>       }
>>
>> +    size_available = memory_region_size(qxl->guest_slots[slot].mr);
>> +    assert(qxl->guest_slots[slot].offset + offset < size_available);
> 
> Can this assertion be triggered by the guest (via an invalid pqxl
> value)? I think the answer is no, but I don't know the the qxl code
> well enough to be sure.

'qxl->guest_slots[slot].offset' is initialized in qxl_add_memslot()
(host); 'size_available' also comes from the host, but 'offset'
comes from the guest via 'QXLPHYSICAL pqxl' IIUC.

I added this check to avoid overflow, but it can be changed to return
an error.

Thanks,

Phil.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 15:25     ` Philippe Mathieu-Daudé
@ 2022-11-28 15:32       ` Stefan Hajnoczi
  2022-11-28 15:46         ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Stefan Hajnoczi @ 2022-11-28 15:32 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Gerd Hoffmann, Mauro Matteo Cascella,
	Paolo Bonzini

On Mon, 28 Nov 2022 at 10:25, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 28/11/22 16:16, Stefan Hajnoczi wrote:
> > On Mon, 28 Nov 2022 at 08:53, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> >>
> >> Have qxl_get_check_slot_offset() return false if the requested
> >> buffer size does not fit within the slot memory region.
> >>
> >> Similarly qxl_phys2virt() now returns NULL in such case, and
> >> qxl_dirty_one_surface() aborts.
> >>
> >> This avoids buffer overrun in the host pointer returned by
> >> memory_region_get_ram_ptr().
> >>
> >> Fixes: CVE-2022-4144 (out-of-bounds read)
> >> Reported-by: Wenxu Yin (@awxylitol)
> >> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
> >> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> >> ---
> >>   hw/display/qxl.c | 22 ++++++++++++++++++----
> >>   hw/display/qxl.h |  2 +-
> >>   2 files changed, 19 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
> >> index 231d733250..afa157d327 100644
> >> --- a/hw/display/qxl.c
> >> +++ b/hw/display/qxl.c
> >> @@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
> >>
> >>   /* can be also called from spice server thread context */
> >>   static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
> >> -                                      uint32_t *s, uint64_t *o)
> >> +                                      uint32_t *s, uint64_t *o,
> >> +                                      size_t size_requested)
> >>   {
> >>       uint64_t phys   = le64_to_cpu(pqxl);
> >>       uint32_t slot   = (phys >> (64 -  8)) & 0xff;
> >>       uint64_t offset = phys & 0xffffffffffff;
> >> +    uint64_t size_available;
> >>
> >>       if (slot >= NUM_MEMSLOTS) {
> >>           qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
> >> @@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
> >>           return false;
> >>       }
> >>
> >> +    size_available = memory_region_size(qxl->guest_slots[slot].mr);
> >> +    assert(qxl->guest_slots[slot].offset + offset < size_available);
> >
> > Can this assertion be triggered by the guest (via an invalid pqxl
> > value)? I think the answer is no, but I don't know the the qxl code
> > well enough to be sure.
>
> 'qxl->guest_slots[slot].offset' is initialized in qxl_add_memslot()
> (host); 'size_available' also comes from the host, but 'offset'
> comes from the guest via 'QXLPHYSICAL pqxl' IIUC.
>
> I added this check to avoid overflow, but it can be changed to return
> an error.

Yes, please.

Aside from concerns about -DNDEBUG, which builds without assertions,
there is also a DoS issue with nested virt where an L2 guest shouldn't
be able to abort the L1 guest's QEMU by triggering an assertion in a
pass through device.

Guest input validation should use explicit error checking code instead
of assert(3).

Thanks,
Stefan


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 15:08   ` Gerd Hoffmann
@ 2022-11-28 15:41     ` Philippe Mathieu-Daudé
  2022-11-28 15:49       ` Gerd Hoffmann
  2022-11-28 16:18       ` Philippe Mathieu-Daudé
  0 siblings, 2 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 15:41 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On 28/11/22 16:08, Gerd Hoffmann wrote:
>> @@ -228,7 +230,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
>>           if (offset == size) {
>>               return;
>>           }
>> -        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
>> +        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, bytes);
>>           if (!chunk) {
>>               return;
>>           }
> 
> Naa, its not that simple.  You get a QXLDataChunk passed in which
> typically is verified *excluding* dynamically-sized chunk->data.

OK so IIUC 1/ this line should be:

   chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id,
                         sizeof(QXLDataChunk));

but 2/ we should check chunk->data[chunk->data_size] is valid (within
the MR) before calling the memcpy(), right?

> Also at least one code path (processing SPICE_CURSOR_TYPE_MONO in
> qxl_cursor) goes access chunk.data[] without calling
> qxl_unpack_chunks(), that needs additional verification too (or
> switch it to call qxl_unpack_chunks, or just drop it because nobody
> uses mono chrome cursors anyway).

OK I'll look at that.

Thanks,

Phil.



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 15:32       ` Stefan Hajnoczi
@ 2022-11-28 15:46         ` Philippe Mathieu-Daudé
  2022-11-28 15:48           ` Stefan Hajnoczi
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 15:46 UTC (permalink / raw)
  To: Stefan Hajnoczi
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Gerd Hoffmann, Mauro Matteo Cascella,
	Paolo Bonzini

On 28/11/22 16:32, Stefan Hajnoczi wrote:
> On Mon, 28 Nov 2022 at 10:25, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>>
>> On 28/11/22 16:16, Stefan Hajnoczi wrote:
>>> On Mon, 28 Nov 2022 at 08:53, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>>>>
>>>> Have qxl_get_check_slot_offset() return false if the requested
>>>> buffer size does not fit within the slot memory region.
>>>>
>>>> Similarly qxl_phys2virt() now returns NULL in such case, and
>>>> qxl_dirty_one_surface() aborts.
>>>>
>>>> This avoids buffer overrun in the host pointer returned by
>>>> memory_region_get_ram_ptr().
>>>>
>>>> Fixes: CVE-2022-4144 (out-of-bounds read)
>>>> Reported-by: Wenxu Yin (@awxylitol)
>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
>>>> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>>>> ---
>>>>    hw/display/qxl.c | 22 ++++++++++++++++++----
>>>>    hw/display/qxl.h |  2 +-
>>>>    2 files changed, 19 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
>>>> index 231d733250..afa157d327 100644
>>>> --- a/hw/display/qxl.c
>>>> +++ b/hw/display/qxl.c
>>>> @@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
>>>>
>>>>    /* can be also called from spice server thread context */
>>>>    static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>>>> -                                      uint32_t *s, uint64_t *o)
>>>> +                                      uint32_t *s, uint64_t *o,
>>>> +                                      size_t size_requested)
>>>>    {
>>>>        uint64_t phys   = le64_to_cpu(pqxl);
>>>>        uint32_t slot   = (phys >> (64 -  8)) & 0xff;
>>>>        uint64_t offset = phys & 0xffffffffffff;
>>>> +    uint64_t size_available;
>>>>
>>>>        if (slot >= NUM_MEMSLOTS) {
>>>>            qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
>>>> @@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
>>>>            return false;
>>>>        }
>>>>
>>>> +    size_available = memory_region_size(qxl->guest_slots[slot].mr);
>>>> +    assert(qxl->guest_slots[slot].offset + offset < size_available);
>>>
>>> Can this assertion be triggered by the guest (via an invalid pqxl
>>> value)? I think the answer is no, but I don't know the the qxl code
>>> well enough to be sure.
>>
>> 'qxl->guest_slots[slot].offset' is initialized in qxl_add_memslot()
>> (host); 'size_available' also comes from the host, but 'offset'
>> comes from the guest via 'QXLPHYSICAL pqxl' IIUC.
>>
>> I added this check to avoid overflow, but it can be changed to return
>> an error.
> 
> Yes, please.

Or I could use Int128 to do arithmetic, but various other places do it
this way without checking overflow with memory_region_size(). Such API
change should be global and is out of the scope of this CVE fix IMO.

> Aside from concerns about -DNDEBUG, which builds without assertions,

This isn't an issue anymore since 262a69f428 ("osdep.h: Prohibit 
disabling assert() in supported builds").

> there is also a DoS issue with nested virt where an L2 guest shouldn't
> be able to abort the L1 guest's QEMU by triggering an assertion in a
> pass through device.
> 
> Guest input validation should use explicit error checking code instead
> of assert(3).

Certainly.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144)
  2022-11-28 15:46         ` Philippe Mathieu-Daudé
@ 2022-11-28 15:48           ` Stefan Hajnoczi
  0 siblings, 0 replies; 20+ messages in thread
From: Stefan Hajnoczi @ 2022-11-28 15:48 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Gerd Hoffmann, Mauro Matteo Cascella,
	Paolo Bonzini

On Mon, 28 Nov 2022 at 10:46, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
>
> On 28/11/22 16:32, Stefan Hajnoczi wrote:
> > On Mon, 28 Nov 2022 at 10:25, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> >>
> >> On 28/11/22 16:16, Stefan Hajnoczi wrote:
> >>> On Mon, 28 Nov 2022 at 08:53, Philippe Mathieu-Daudé <philmd@linaro.org> wrote:
> >>>>
> >>>> Have qxl_get_check_slot_offset() return false if the requested
> >>>> buffer size does not fit within the slot memory region.
> >>>>
> >>>> Similarly qxl_phys2virt() now returns NULL in such case, and
> >>>> qxl_dirty_one_surface() aborts.
> >>>>
> >>>> This avoids buffer overrun in the host pointer returned by
> >>>> memory_region_get_ram_ptr().
> >>>>
> >>>> Fixes: CVE-2022-4144 (out-of-bounds read)
> >>>> Reported-by: Wenxu Yin (@awxylitol)
> >>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1336
> >>>> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> >>>> ---
> >>>>    hw/display/qxl.c | 22 ++++++++++++++++++----
> >>>>    hw/display/qxl.h |  2 +-
> >>>>    2 files changed, 19 insertions(+), 5 deletions(-)
> >>>>
> >>>> diff --git a/hw/display/qxl.c b/hw/display/qxl.c
> >>>> index 231d733250..afa157d327 100644
> >>>> --- a/hw/display/qxl.c
> >>>> +++ b/hw/display/qxl.c
> >>>> @@ -1424,11 +1424,13 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
> >>>>
> >>>>    /* can be also called from spice server thread context */
> >>>>    static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
> >>>> -                                      uint32_t *s, uint64_t *o)
> >>>> +                                      uint32_t *s, uint64_t *o,
> >>>> +                                      size_t size_requested)
> >>>>    {
> >>>>        uint64_t phys   = le64_to_cpu(pqxl);
> >>>>        uint32_t slot   = (phys >> (64 -  8)) & 0xff;
> >>>>        uint64_t offset = phys & 0xffffffffffff;
> >>>> +    uint64_t size_available;
> >>>>
> >>>>        if (slot >= NUM_MEMSLOTS) {
> >>>>            qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
> >>>> @@ -1453,6 +1455,18 @@ static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
> >>>>            return false;
> >>>>        }
> >>>>
> >>>> +    size_available = memory_region_size(qxl->guest_slots[slot].mr);
> >>>> +    assert(qxl->guest_slots[slot].offset + offset < size_available);
> >>>
> >>> Can this assertion be triggered by the guest (via an invalid pqxl
> >>> value)? I think the answer is no, but I don't know the the qxl code
> >>> well enough to be sure.
> >>
> >> 'qxl->guest_slots[slot].offset' is initialized in qxl_add_memslot()
> >> (host); 'size_available' also comes from the host, but 'offset'
> >> comes from the guest via 'QXLPHYSICAL pqxl' IIUC.
> >>
> >> I added this check to avoid overflow, but it can be changed to return
> >> an error.
> >
> > Yes, please.
>
> Or I could use Int128 to do arithmetic, but various other places do it
> this way without checking overflow with memory_region_size(). Such API
> change should be global and is out of the scope of this CVE fix IMO.
>
> > Aside from concerns about -DNDEBUG, which builds without assertions,
>
> This isn't an issue anymore since 262a69f428 ("osdep.h: Prohibit
> disabling assert() in supported builds").

I didn't know about that. Thanks!

Stefan


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 15:41     ` Philippe Mathieu-Daudé
@ 2022-11-28 15:49       ` Gerd Hoffmann
  2022-11-28 16:18       ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2022-11-28 15:49 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On Mon, Nov 28, 2022 at 04:41:14PM +0100, Philippe Mathieu-Daudé wrote:
> On 28/11/22 16:08, Gerd Hoffmann wrote:
> > > @@ -228,7 +230,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
> > >           if (offset == size) {
> > >               return;
> > >           }
> > > -        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
> > > +        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, bytes);
> > >           if (!chunk) {
> > >               return;
> > >           }
> > 
> > Naa, its not that simple.  You get a QXLDataChunk passed in which
> > typically is verified *excluding* dynamically-sized chunk->data.
> 
> OK so IIUC 1/ this line should be:
> 
>   chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id,
>                         sizeof(QXLDataChunk));

Depends on whenever you do (2) inside or outside the loop ;)

> but 2/ we should check chunk->data[chunk->data_size] is valid (within
> the MR) before calling the memcpy(), right?

Yes.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 15:41     ` Philippe Mathieu-Daudé
  2022-11-28 15:49       ` Gerd Hoffmann
@ 2022-11-28 16:18       ` Philippe Mathieu-Daudé
  2022-11-28 16:29         ` Philippe Mathieu-Daudé
  1 sibling, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 16:18 UTC (permalink / raw)
  To: Gerd Hoffmann, Peter Wu
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On 28/11/22 16:41, Philippe Mathieu-Daudé wrote:
> On 28/11/22 16:08, Gerd Hoffmann wrote:

>> Also at least one code path (processing SPICE_CURSOR_TYPE_MONO in
>> qxl_cursor) goes access chunk.data[] without calling
>> qxl_unpack_chunks(), that needs additional verification too (or
>> switch it to call qxl_unpack_chunks, or just drop it because nobody
>> uses mono chrome cursors anyway).
Per commit 36ffc122dc ("qxl: support mono cursors with inverted colors")
"Monochrome cursors are still used by Windows guests" (i.e. Win2008R2)
:/


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 16:18       ` Philippe Mathieu-Daudé
@ 2022-11-28 16:29         ` Philippe Mathieu-Daudé
  2022-11-28 16:52           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 16:29 UTC (permalink / raw)
  To: Gerd Hoffmann, Peter Wu
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On 28/11/22 17:18, Philippe Mathieu-Daudé wrote:
> On 28/11/22 16:41, Philippe Mathieu-Daudé wrote:
>> On 28/11/22 16:08, Gerd Hoffmann wrote:
> 
>>> Also at least one code path (processing SPICE_CURSOR_TYPE_MONO in
>>> qxl_cursor) goes access chunk.data[] without calling
>>> qxl_unpack_chunks(), that needs additional verification too (or
>>> switch it to call qxl_unpack_chunks, or just drop it because nobody
>>> uses mono chrome cursors anyway).
> Per commit 36ffc122dc ("qxl: support mono cursors with inverted colors")
> "Monochrome cursors are still used by Windows guests" (i.e. Win2008R2)
> :/

Hmm I guess I'm missing something in qxl_cursor() following the
SPICE_CURSOR_TYPE_MONO case.

- cursor_alloc() allocate QEMUCursor* c but doesn't set c->data,
- nothing seems to set c->data
- cursor_set_mono() is called and *(c->data) is assigned...

?



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()
  2022-11-28 16:29         ` Philippe Mathieu-Daudé
@ 2022-11-28 16:52           ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 20+ messages in thread
From: Philippe Mathieu-Daudé @ 2022-11-28 16:52 UTC (permalink / raw)
  To: Gerd Hoffmann, Peter Wu
  Cc: Marc-André Lureau, qemu-devel, Alexander Bulekov,
	Peter Maydell, Mauro Matteo Cascella, Paolo Bonzini

On 28/11/22 17:29, Philippe Mathieu-Daudé wrote:
> On 28/11/22 17:18, Philippe Mathieu-Daudé wrote:
>> On 28/11/22 16:41, Philippe Mathieu-Daudé wrote:
>>> On 28/11/22 16:08, Gerd Hoffmann wrote:
>>
>>>> Also at least one code path (processing SPICE_CURSOR_TYPE_MONO in
>>>> qxl_cursor) goes access chunk.data[] without calling
>>>> qxl_unpack_chunks(), that needs additional verification too (or
>>>> switch it to call qxl_unpack_chunks, or just drop it because nobody
>>>> uses mono chrome cursors anyway).
>> Per commit 36ffc122dc ("qxl: support mono cursors with inverted colors")
>> "Monochrome cursors are still used by Windows guests" (i.e. Win2008R2)
>> :/
> 
> Hmm I guess I'm missing something in qxl_cursor() following the
> SPICE_CURSOR_TYPE_MONO case.
> 
> - cursor_alloc() allocate QEMUCursor* c but doesn't set c->data,

Sorry long day, cursor_alloc() does allocate c->data:

typedef struct QEMUCursor {
     int                 width, height;
     int                 hot_x, hot_y;
     int                 refcount;
     uint32_t            data[];
} QEMUCursor;

QEMUCursor *cursor_alloc(int width, int height)
{
     QEMUCursor *c;
     size_t datasize = width * height * sizeof(uint32_t);

     if (width > 512 || height > 512) {
         return NULL;
     }

     c = g_malloc0(sizeof(QEMUCursor) + datasize);


> - nothing seems to set c->data
> - cursor_set_mono() is called and *(c->data) is assigned...
> 
> ?
> 



^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2022-11-28 16:52 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-28 13:48 [RFC PATCH-for-7.2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
2022-11-28 13:48 ` [PATCH-for-7.2 1/5] hw/display/qxl: Have qxl_log_command Return early if no log_cmd handler Philippe Mathieu-Daudé
2022-11-28 13:48 ` [PATCH-for-7.2 2/5] hw/display/qxl: Document qxl_phys2virt() Philippe Mathieu-Daudé
2022-11-28 13:48 ` [RFC PATCH-for-7.2 3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt() Philippe Mathieu-Daudé
2022-11-28 13:53   ` Marc-André Lureau
2022-11-28 15:08   ` Gerd Hoffmann
2022-11-28 15:41     ` Philippe Mathieu-Daudé
2022-11-28 15:49       ` Gerd Hoffmann
2022-11-28 16:18       ` Philippe Mathieu-Daudé
2022-11-28 16:29         ` Philippe Mathieu-Daudé
2022-11-28 16:52           ` Philippe Mathieu-Daudé
2022-11-28 13:48 ` [RFC PATCH-for-7.2 4/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt (CVE-2022-4144) Philippe Mathieu-Daudé
2022-11-28 15:16   ` Stefan Hajnoczi
2022-11-28 15:25     ` Philippe Mathieu-Daudé
2022-11-28 15:32       ` Stefan Hajnoczi
2022-11-28 15:46         ` Philippe Mathieu-Daudé
2022-11-28 15:48           ` Stefan Hajnoczi
2022-11-28 13:48 ` [PATCH-for-8.0 5/5] hw/display/qxl: Assert memory slot fits in preallocated MemoryRegion Philippe Mathieu-Daudé
2022-11-28 13:51 ` [RFC PATCH-for-7.2 v2 0/5] hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() Philippe Mathieu-Daudé
2022-11-28 15:19 ` [RFC PATCH-for-7.2 " Stefan Hajnoczi

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.