diff mbox series

[RFC,PATCH-for-7.2,3/5] hw/display/qxl: Pass requested buffer size to qxl_phys2virt()

Message ID 20221128134832.84867-4-philmd@linaro.org
State Superseded
Headers show
Series hw/display/qxl: Avoid buffer overrun in qxl_phys2virt() | expand

Commit Message

Philippe Mathieu-Daudé Nov. 28, 2022, 1:48 p.m. UTC
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(-)

Comments

Marc-André Lureau Nov. 28, 2022, 1:53 p.m. UTC | #1
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
>
>
Gerd Hoffmann Nov. 28, 2022, 3:08 p.m. UTC | #2
> @@ -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
Philippe Mathieu-Daudé Nov. 28, 2022, 3:41 p.m. UTC | #3
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.
Gerd Hoffmann Nov. 28, 2022, 3:49 p.m. UTC | #4
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
Philippe Mathieu-Daudé Nov. 28, 2022, 4:18 p.m. UTC | #5
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)
:/
Philippe Mathieu-Daudé Nov. 28, 2022, 4:29 p.m. UTC | #6
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...

?
Philippe Mathieu-Daudé Nov. 28, 2022, 4:52 p.m. UTC | #7
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...
> 
> ?
>
diff mbox series

Patch

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);