vscsi_send_adapter_info() used the guest-supplied common.length
field directly as the size argument to spapr_vio_dma_write().
Since common.length is controlled by the guest, a value larger
than sizeof(struct mad_adapter_info_data) (148 bytes) caused
dma_memory_write() to read past the end of the stack-allocated info
struct, leaking arbitrary host stack contents into guest memory.

Fix this by clamping the write length to sizeof(info), then using
spapr_vio_dma_set() to zero-fill any remaining bytes the guest
requested beyond that size. This mirrors the existing pattern in
vscsi_send_capabilities().

Closes: https://gitlab.com/qemu-project/qemu/-/work_items/4626
Reported-by: Guthrie McAfee Armstrong <[email protected]>
Signed-off-by: Chinmay Rath <[email protected]>
---
 hw/scsi/spapr_vscsi.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c
index b4c8f94d22..7ec780da79 100644
--- a/hw/scsi/spapr_vscsi.c
+++ b/hw/scsi/spapr_vscsi.c
@@ -966,6 +966,8 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req 
*req)
 {
     struct viosrp_adapter_info *sinfo;
     struct mad_adapter_info_data info;
+    uint16_t len, req_len;
+    uint64_t buffer;
     int rc;
 
     sinfo = &req_iu(req)->mad.adapter_info;
@@ -985,11 +987,23 @@ static int vscsi_send_adapter_info(VSCSIState *s, 
vscsi_req *req)
     info.os_type = cpu_to_be32(2);
     info.port_max_txu[0] = cpu_to_be32(VSCSI_MAX_SECTORS << 9);
 
-    rc = spapr_vio_dma_write(&s->vdev, be64_to_cpu(sinfo->buffer),
-                             &info, be16_to_cpu(sinfo->common.length));
+    req_len = len = be16_to_cpu(sinfo->common.length);
+    buffer = be64_to_cpu(sinfo->buffer);
+    if (len > sizeof(info)) {
+        fprintf(stderr, "vscsi_send_adapter_info: adapter info size mismatch 
!\n");
+        /*
+         * Just read and populate the structure that is known.
+         * Zero rest of the structure.
+         */
+        len = sizeof(info);
+    }
+    rc = spapr_vio_dma_write(&s->vdev, buffer, &info, len);
     if (rc)  {
         fprintf(stderr, "vscsi_send_adapter_info: DMA write failure !\n");
     }
+    if (req_len > len) {
+        spapr_vio_dma_set(&s->vdev, (buffer + len), 0, (req_len - len));
+    }
 
     sinfo->common.status = rc ? cpu_to_be32(1) : 0;
 
-- 
2.55.0


Reply via email to