* [PATCH] cxl/mbox: bound the device-supplied command return code
@ 2026-09-29 6:07 Gaobin Huang
2026-10-14 1:36 ` Alison Schofield
0 siblings, 1 reply; 4+ messages in thread
From: Gaobin Huang @ 2026-09-29 6:07 UTC (permalink / raw)
To: linux-cxl
Cc: Davidlohr Bueso, Jonathan Cameron, Dave Jiang, Alison Schofield,
Vishal Verma, Dan Williams, Ira Weiny, Li Ming, Richard Cheng,
linux-kernel
cxl_pci_mbox_send() takes the command return code straight out of the
mailbox status register:
mbox_cmd->return_code =
FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
The field is 16 bits wide and nothing validates it. It is read the same
way from the background command status register when a background
operation completes (CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK, also 16 bits).
Both consumers then index a table with it:
return cxl_mbox_cmd_rctable[mbox_cmd->return_code].err;
and that table, built from CMD_CMD_RC_TABLE, has 31 entries (SUCCESS(0)
through EXTLIST(0x1e)). A device that reports 0x1f or above therefore
reads past the end of a global array.
cxl_mbox_cmd_rc2errno() is called for every non-success, non-background
return code, which makes this reachable during probe with no host action:
answering Get Supported Logs (0x0400) with return code 0x1f is enough.
Reproduced with a QEMU Type-3 device that reports a chosen return code,
against this tree:
BUG: KASAN: global-out-of-bounds in cxl_internal_send_cmd+0x11d/0x190
Read of size 4 at addr ffffffffa5212c10 by task kworker/u8:2/37
Call Trace:
cxl_internal_send_cmd+0x11d/0x190
cxl_enumerate_cmds+0x12c/0x980
cxl_pci_probe+0x84a/0x11e0
Return codes that are in the table take the ordinary error path and
report nothing, which is what makes the out-of-range case attributable
rather than just "the device reported an error".
Validate once, in a helper that both accessors use, rather than at the
two register read sites: the same bound then covers the foreground and
the background path, and any future consumer of the field. An undefined
code is still a failure, and -ENXIO is what every non-success entry in
the table already maps to, so callers cannot tell the difference in a way
that matters.
Fixes: 92fcc1abab09 ("cxl/mbox: Improve handling of mbox_cmd hw return codes")
Signed-off-by: Gaobin Huang <huanggaobin23@semi.ac.cn>
---
drivers/cxl/cxlmem.h | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
index c401e3a1a..7f89dc4fc 100644
--- a/drivers/cxl/cxlmem.h
+++ b/drivers/cxl/cxlmem.h
@@ -6,6 +6,7 @@
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/uuid.h>
+#include <linux/array_size.h>
#include <linux/node.h>
#include <cxl/event.h>
#include <cxl/mailbox.h>
@@ -205,14 +206,39 @@ static const
struct cxl_mbox_cmd_rc cxl_mbox_cmd_rctable[] ={ CMD_CMD_RC_TABLE };
#undef C
+/*
+ * The return code comes straight out of a device register: the low 16 bits of
+ * CXL_DEV_MAILBOX_STS.ERRNO for a foreground command, and the same width of
+ * CXL_DEV_BG_CMD_STS.RET_CODE for a background one. Nothing validates it, so
+ * a device that reports an undefined code would index past the end of the
+ * table above with both of the accessors below.
+ */
+static inline const struct cxl_mbox_cmd_rc *
+cxl_mbox_cmd_rc_entry(struct cxl_mbox_cmd *mbox_cmd)
+{
+ if (mbox_cmd->return_code >= ARRAY_SIZE(cxl_mbox_cmd_rctable))
+ return NULL;
+
+ return &cxl_mbox_cmd_rctable[mbox_cmd->return_code];
+}
+
static inline const char *cxl_mbox_cmd_rc2str(struct cxl_mbox_cmd *mbox_cmd)
{
- return cxl_mbox_cmd_rctable[mbox_cmd->return_code].desc;
+ const struct cxl_mbox_cmd_rc *rc = cxl_mbox_cmd_rc_entry(mbox_cmd);
+
+ return rc ? rc->desc : "undefined command return code";
}
static inline int cxl_mbox_cmd_rc2errno(struct cxl_mbox_cmd *mbox_cmd)
{
- return cxl_mbox_cmd_rctable[mbox_cmd->return_code].err;
+ const struct cxl_mbox_cmd_rc *rc = cxl_mbox_cmd_rc_entry(mbox_cmd);
+
+ /*
+ * Unknown codes are still failures, and -ENXIO is what every
+ * non-success entry in the table maps to, so the caller cannot tell
+ * an undefined code from a defined error in a way that matters.
+ */
+ return rc ? rc->err : -ENXIO;
}
/*
base-commit: 999811aca000b0d3d1c838c60dc9db7c72eb0c73
--
2.34.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] cxl/mbox: bound the device-supplied command return code
2026-09-29 6:07 [PATCH] cxl/mbox: bound the device-supplied command return code Gaobin Huang
@ 2026-10-14 1:36 ` Alison Schofield
2026-10-14 14:48 ` 黄高彬
0 siblings, 1 reply; 4+ messages in thread
From: Alison Schofield @ 2026-10-14 1:36 UTC (permalink / raw)
To: Gaobin Huang
Cc: linux-cxl, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
Vishal Verma, Dan Williams, Ira Weiny, Li Ming, Richard Cheng,
linux-kernel
On Tue, Sep 29, 2026 at 02:07:47PM +0800, Gaobin Huang wrote:
> cxl_pci_mbox_send() takes the command return code straight out of the
> mailbox status register:
>
> mbox_cmd->return_code =
> FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
>
> The field is 16 bits wide and nothing validates it. It is read the same
> way from the background command status register when a background
> operation completes (CXLDEV_MBOX_BG_CMD_COMMAND_RC_MASK, also 16 bits).
> Both consumers then index a table with it:
>
> return cxl_mbox_cmd_rctable[mbox_cmd->return_code].err;
>
> and that table, built from CMD_CMD_RC_TABLE, has 31 entries (SUCCESS(0)
> through EXTLIST(0x1e)). A device that reports 0x1f or above therefore
> reads past the end of a global array.
>
> cxl_mbox_cmd_rc2errno() is called for every non-success, non-background
> return code, which makes this reachable during probe with no host action:
> answering Get Supported Logs (0x0400) with return code 0x1f is enough.
>
> Reproduced with a QEMU Type-3 device that reports a chosen return code,
> against this tree:
>
> BUG: KASAN: global-out-of-bounds in cxl_internal_send_cmd+0x11d/0x190
> Read of size 4 at addr ffffffffa5212c10 by task kworker/u8:2/37
> Call Trace:
> cxl_internal_send_cmd+0x11d/0x190
> cxl_enumerate_cmds+0x12c/0x980
> cxl_pci_probe+0x84a/0x11e0
>
> Return codes that are in the table take the ordinary error path and
> report nothing, which is what makes the out-of-range case attributable
> rather than just "the device reported an error".
>
> Validate once, in a helper that both accessors use, rather than at the
> two register read sites: the same bound then covers the foreground and
> the background path, and any future consumer of the field. An undefined
> code is still a failure, and -ENXIO is what every non-success entry in
> the table already maps to, so callers cannot tell the difference in a way
> that matters.
>
Hi Gaobin,
Thanks for the patch!
I'm taking this into a series that adds CXL KUnit tests, including a regression
test for this table. You'll get CC'd when it posts and I'll need you ACK it
since it has your original authorship with changes from me added.
(Sashiko is right here. PADDR and POISONLMT don't map to -ENXIO, so the comment
and changelog claim isn't accurate. -ENXIO is still the right fallback for an
unknown code.)
It looks like the return code you saw is valid for CXL 4.0 and just not added to
CXL driver yet. I don't think anyone else has sent a patch for that yet,
so you can if you like.
This is what I'm looking at:
CXL 4.0, Table 8-208 "CXL-defined Command Return Codes" (section 8.2.9.4.5.1):
001Fh and 0020h
-- Alison
> Fixes: 92fcc1abab09 ("cxl/mbox: Improve handling of mbox_cmd hw return codes")
> Signed-off-by: Gaobin Huang <huanggaobin23@semi.ac.cn>
> ---
> drivers/cxl/cxlmem.h | 30 ++++++++++++++++++++++++++++--
> 1 file changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
> index c401e3a1a..7f89dc4fc 100644
> --- a/drivers/cxl/cxlmem.h
> +++ b/drivers/cxl/cxlmem.h
> @@ -6,6 +6,7 @@
> #include <linux/pci.h>
> #include <linux/cdev.h>
> #include <linux/uuid.h>
> +#include <linux/array_size.h>
> #include <linux/node.h>
> #include <cxl/event.h>
> #include <cxl/mailbox.h>
> @@ -205,14 +206,39 @@ static const
> struct cxl_mbox_cmd_rc cxl_mbox_cmd_rctable[] ={ CMD_CMD_RC_TABLE };
> #undef C
>
> +/*
> + * The return code comes straight out of a device register: the low 16 bits of
> + * CXL_DEV_MAILBOX_STS.ERRNO for a foreground command, and the same width of
> + * CXL_DEV_BG_CMD_STS.RET_CODE for a background one. Nothing validates it, so
> + * a device that reports an undefined code would index past the end of the
> + * table above with both of the accessors below.
> + */
> +static inline const struct cxl_mbox_cmd_rc *
> +cxl_mbox_cmd_rc_entry(struct cxl_mbox_cmd *mbox_cmd)
> +{
> + if (mbox_cmd->return_code >= ARRAY_SIZE(cxl_mbox_cmd_rctable))
> + return NULL;
> +
> + return &cxl_mbox_cmd_rctable[mbox_cmd->return_code];
> +}
> +
> static inline const char *cxl_mbox_cmd_rc2str(struct cxl_mbox_cmd *mbox_cmd)
> {
> - return cxl_mbox_cmd_rctable[mbox_cmd->return_code].desc;
> + const struct cxl_mbox_cmd_rc *rc = cxl_mbox_cmd_rc_entry(mbox_cmd);
> +
> + return rc ? rc->desc : "undefined command return code";
> }
>
> static inline int cxl_mbox_cmd_rc2errno(struct cxl_mbox_cmd *mbox_cmd)
> {
> - return cxl_mbox_cmd_rctable[mbox_cmd->return_code].err;
> + const struct cxl_mbox_cmd_rc *rc = cxl_mbox_cmd_rc_entry(mbox_cmd);
> +
> + /*
> + * Unknown codes are still failures, and -ENXIO is what every
> + * non-success entry in the table maps to, so the caller cannot tell
> + * an undefined code from a defined error in a way that matters.
> + */
> + return rc ? rc->err : -ENXIO;
> }
>
> /*
> base-commit: 999811aca000b0d3d1c838c60dc9db7c72eb0c73
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Re: [PATCH] cxl/mbox: bound the device-supplied command return code
2026-10-14 1:36 ` Alison Schofield
@ 2026-10-14 14:48 ` 黄高彬
2026-10-14 21:39 ` Alison Schofield
0 siblings, 1 reply; 4+ messages in thread
From: 黄高彬 @ 2026-10-14 14:48 UTC (permalink / raw)
To: Alison Schofield
Cc: linux-cxl, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
Vishal Verma, Dan Williams, Ira Weiny, Li Ming, Richard Cheng,
linux-kernel
Hi Alison,
Thank you for picking it up, and for the corrections.
> (Sashiko is right here. PADDR and POISONLMT don't map to -ENXIO, so the
> comment and changelog claim isn't accurate. -ENXIO is still the right
> fallback for an unknown code.)
You are both right: the claim should be that an unknown code is still a failure
and that -ENXIO keeps it in the usual device-error class -- not that every
non-success entry maps to -ENXIO. Please use that wording in the version you
carry; I will not repost the patch on its own.
> I'm taking this into a series that adds CXL KUnit tests, including a
> regression test for this table. You'll get CC'd when it posts and I'll need
> you ACK it since it has your original authorship with changes from me added.
Understood -- I will ACK as soon as it posts.
> It looks like the return code you saw is valid for CXL 4.0 and just not added
> to CXL driver yet. ... CXL 4.0, Table 8-208 ... 001Fh and 0020h
> I don't think anyone else has sent a patch for that yet, so you can if you
> like.
Two things there, and I would rather not collide with your series:
- The KASAN reproduction used 0x1f, which stops being out of range once 001Fh is
in the table. Out of range is any code past the end of the table, so with
001Fh and 0020h added it is 0021h upwards -- the bound itself is unchanged.
- Adding the two entries is a separate change from the bound. Do you want it in
your series next to the KUnit test, so the boundary and the test move
together, or as a standalone patch from me after yours? Either is fine. If
you would rather have it from me, I will take the names and errno mappings
from Table 8-208 and send it separately.
Thanks,
Gaobin
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Re: [PATCH] cxl/mbox: bound the device-supplied command return code
2026-10-14 14:48 ` 黄高彬
@ 2026-10-14 21:39 ` Alison Schofield
0 siblings, 0 replies; 4+ messages in thread
From: Alison Schofield @ 2026-10-14 21:39 UTC (permalink / raw)
To: 黄高彬
Cc: linux-cxl, Davidlohr Bueso, Jonathan Cameron, Dave Jiang,
Vishal Verma, Dan Williams, Ira Weiny, Li Ming, Richard Cheng,
linux-kernel
On Wed, Oct 14, 2026 at 10:48:51PM +0800, 黄高彬 wrote:
> Hi Alison,
>
> Thank you for picking it up, and for the corrections.
>
> > (Sashiko is right here. PADDR and POISONLMT don't map to -ENXIO, so the
> > comment and changelog claim isn't accurate. -ENXIO is still the right
> > fallback for an unknown code.)
>
> You are both right: the claim should be that an unknown code is still a failure
> and that -ENXIO keeps it in the usual device-error class -- not that every
> non-success entry maps to -ENXIO. Please use that wording in the version you
> carry; I will not repost the patch on its own.
The version in the v1 cxl-kunit patchset[1] already avoids that claim. It reports
unknown codes as -ENXIO, "the errno used for most device-reported errors".
>
> > I'm taking this into a series that adds CXL KUnit tests, including a
> > regression test for this table. You'll get CC'd when it posts and I'll need
> > you ACK it since it has your original authorship with changes from me added.
>
> Understood -- I will ACK as soon as it posts.
>
> > It looks like the return code you saw is valid for CXL 4.0 and just not added
> > to CXL driver yet. ... CXL 4.0, Table 8-208 ... 001Fh and 0020h
> > I don't think anyone else has sent a patch for that yet, so you can if you
> > like.
>
> Two things there, and I would rather not collide with your series:
>
> - The KASAN reproduction used 0x1f, which stops being out of range once 001Fh is
> in the table. Out of range is any code past the end of the table, so with
> 001Fh and 0020h added it is 0021h upwards -- the bound itself is unchanged.
> - Adding the two entries is a separate change from the bound. Do you want it in
> your series next to the KUnit test, so the boundary and the test move
> together, or as a standalone patch from me after yours? Either is fine. If
> you would rather have it from me, I will take the names and errno mappings
> from Table 8-208 and send it separately.
Standalone patch from you please. It doesn't need to wait on the series.
The table can be extended without touching the test.
The test has 2 cases that read the table:
- Walk every code in the table and check each errno return. It loops up
to the table size, so the new entries your patch adds will get picked
up automatically.
- Checks codes past the end of the table. Its first code is the table
size itself, so today it's 001Fh and w your patch it'll be 0021h.
The test only needs a change if a new code maps to an errno other than
-ENXIO.
This conversation made me aware that the testing page could say more
about maintenance of the unit tests. Take a look here [2]
[1] https://lore.kernel.org/linux-cxl/3ed8f5db3f21c937d8783f31ee6faca3703ba72a.1791949910.git.alison.schofield@intel.com/
[2] https://lore.kernel.org/linux-cxl/as_1dGrwsPYTlZrp@aschofie-mobl2.lan/
-- Alison
>
> Thanks,
> Gaobin
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-14 21:39 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-29 6:07 [PATCH] cxl/mbox: bound the device-supplied command return code Gaobin Huang
2026-10-14 1:36 ` Alison Schofield
2026-10-14 14:48 ` 黄高彬
2026-10-14 21:39 ` Alison Schofield
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®