Commit 292902ae4e for qemu.org

commit 292902ae4e93dcf33414ec3c6951e02d497cc850
Author: Haotian Jiang <jianghaotian.sunday@gmail.com>
Date:   Mon Jul 13 15:23:36 2026 +0800

    hw/cxl: fix OOB access in cxl_doe_cdat_rsp via entry_handle

    cxl_doe_cdat_rsp() takes ent = req->entry_handle (uint16_t, fully
    guest-controlled, 0..0xFFFF) and directly indexes cdat->entry[ent]
    without checking ent < cdat->entry_len. For a default cxl-type3 with
    one volatile memory region, entry_len = 1 + CT3_CDAT_NUM_ENTRIES = 7,
    so any entry_handle >= 7 reads past the CDATEntry array into host heap.

    The OOB-read base/length are then used in
    memcpy(read_mbox + offset, base, len) at cxl_type3.c:298-299, leaking
    host heap memory to the guest via PCI_EXP_DOE_RD_DATA_MBOX, and
    potentially overflowing the 1 MiB read_mbox heap buffer when the OOB
    length field is large.

    The same bug exists in the cxl-upstream implementation.

    Existing checks do not bound ent: assert(cdat->entry_len) only ensures
    the table is loaded; the minimum-length check only guards against a
    truncated CDATReq; the entry_handle ternary at line 293 only decides
    the next-handle echo, not the current access; pcie_doe_get_obj_len
    reads header.length, not entry_handle.

    Reproduce: build QEMU with CONFIG_CXL, boot
      -M q35,cxl=on -device pxb-cxl,bus_nr=52 ... -device cxl-type3,...
    then send a CDATReq with entry_handle=0xFFFF via the DOE mailbox at
    config offset 0x190. Under ASAN this reports SEGV in cxl_doe_cdat_rsp
    at cxl_type3.c:281.

    Fixes: f5ee7413d5 ("hw/mem/cxl-type3: Add CXL CDAT Data Object Exchange")
    Fixes: 882877fc35 ("hw/pci-bridge/cxl-upstream: Add a CDAT table access DOE")
    Signed-off-by: Haotian Jiang <jianghaotian.sunday@gmail.com>
    Cc: qemu-stable@nongnu.org
    Reviewed-by: Jonathan Cameron <jic23@kernel.org>
    Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
    Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
    Message-ID: <20260713072336.623604-2-jianghaotian.sunday@gmail.com>

diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
index cba05ec57d..28f41fa623 100644
--- a/hw/mem/cxl_type3.c
+++ b/hw/mem/cxl_type3.c
@@ -278,6 +278,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap)
     }

     ent = req->entry_handle;
+    if (ent >= cdat->entry_len) {
+        return false;
+    }
     base = cdat->entry[ent].base;
     len = cdat->entry[ent].length;

diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c
index b6281cbd4c..69773ff163 100644
--- a/hw/pci-bridge/cxl_upstream.c
+++ b/hw/pci-bridge/cxl_upstream.c
@@ -158,6 +158,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap)
     }

     ent = req->entry_handle;
+    if (ent >= cdat->entry_len) {
+        return false;
+    }
     base = cdat->entry[ent].base;
     len = cdat->entry[ent].length;