[PATCH 2/2] cxl/mbox: bound the Get Supported Logs entry count by the payload

From: Gaobin Huang

Date: Mon Sep 14 2026 - 10:01:27 EST


cxl_enumerate_cmds() iterates gsl->entries entries of gsl->entry[] using
the count the device put in the Get Supported Logs response. The response
is only checked with .min_out = 2, so a device may report more entries
than it delivered and the driver reads past the end of the buffer. This
is probe time, so it happens on every boot of a machine with such a
device, without any host action.

cxl_get_gsl() can already tell the caller how much arrived, because
__cxl_pci_mbox_send_cmd() records the copied byte count in
mbox_cmd.size_out. Derive the number of entries that fit from it and stop
the loop there.
Guard the subtraction too: min_out is smaller than the response header, so
a two byte response would otherwise wrap the size_t arithmetic and leave
the bound with nothing to do.

Seen with a QEMU Type-3 device that reports 0xffff entries while writing
one, in a 2048 byte buffer:

BUG: KASAN: slab-out-of-bounds in cxl_enumerate_cmds+0x1e3/0x980
Read of size 4 at addr ffff888003707810 by task kworker/u8:2/38
cxl_enumerate_cmds+0x1e3/0x980
cxl_pci_probe+0x84a/0x11e0
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 16 bytes to the right of the
allocated region

The Read of size 4 is gsl->entry[i].size. The buffer can hold 102
entries, so claiming 102 is still inside it and 103 is not, which is what
the sweep shows. With the bound in place the same device logs

GSL: device claimed 65535 entries but the payload holds 1

and enumeration continues with the entries that are present.

This is the cross-check get_supported_features() already performs in
drivers/cxl/core/features.c, where a device supplied count is compared
against the retrieved length before the entries are used.

Fixes: 472b1ce6e9d6 ("cxl/mem: Enable commands via CEL")
Signed-off-by: Gaobin Huang <huanggaobin23@xxxxxxxxxx>
---
drivers/cxl/core/mbox.c | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index a8f51bf0f..c94439554 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -794,7 +794,8 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel)
set_features_cap(cxl_mbox, ro_cmds, wr_cmds);
}

-static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds)
+static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *mds,
+ size_t *len)
{
struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox;
struct cxl_mbox_get_supported_logs *ret;
@@ -818,7 +819,7 @@ static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_memdev_state *
return ERR_PTR(rc);
}

-
+ *len = mbox_cmd.size_out; /* bytes actually received */
return ret;
}

@@ -849,18 +850,39 @@ int cxl_enumerate_cmds(struct cxl_memdev_state *mds)
struct cxl_mbox_get_supported_logs *gsl;
struct device *dev = mds->cxlds.dev;
struct cxl_mem_command *cmd;
+ size_t gsl_len, gsl_hdr, max_entries;
int i, rc;

- gsl = cxl_get_gsl(mds);
+ gsl = cxl_get_gsl(mds, &gsl_len);
if (IS_ERR(gsl))
return PTR_ERR(gsl);

+ /*
+ * The device chooses the reported payload length and may return a
+ * response as short as the entry count field on its own (min_out is
+ * 2), so derive the entry count without underflowing.
+ */
+ gsl_hdr = offsetof(struct cxl_mbox_get_supported_logs, entry);
+ max_entries = gsl_len > gsl_hdr ?
+ (gsl_len - gsl_hdr) / sizeof(gsl->entry[0]) : 0;
+
rc = -ENOENT;
for (i = 0; i < le16_to_cpu(gsl->entries); i++) {
- u32 size = le32_to_cpu(gsl->entry[i].size);
- uuid_t uuid = gsl->entry[i].uuid;
+ u32 size;
+ uuid_t uuid;
u8 *log;

+ if (i >= max_entries) {
+ dev_warn_ratelimited(dev,
+ "GSL: device claimed %u entries but the payload holds %zu\n",
+ le16_to_cpu(gsl->entries),
+ max_entries);
+ break;
+ }
+
+ size = le32_to_cpu(gsl->entry[i].size);
+ uuid = gsl->entry[i].uuid;
+
dev_dbg(dev, "Found LOG type %pU of size %d", &uuid, size);

if (!uuid_equal(&uuid, &log_uuid[CEL_UUID]))
--
2.34.1