From 574eda81d0a7238af3268e88f9effe22953460bc Mon Sep 17 00:00:00 2001 From: Li Ming Date: Thu, 23 Apr 2026 19:19:49 +0800 Subject: [PATCH 01/18] cxl/memdev: Hold memdev lock during memdev poison injection/clear cxl_dpa_to_region() assumes that it is running a context where it is not racing changes to "cxlmd->dev.driver". Acquire the memdev device lock in the debugfs entry points to preclude debugfs usage racing cxl_mem driver detach. Suggested-by: Dan Williams Reviewed-by: Dave Jiang Reviewed-by: Alison Schofield Reviewed-by: Dan Williams Signed-off-by: Li Ming Link: https://patch.msgid.link/20260423111949.177399-1-ming.li@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/mem.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cxl/mem.c b/drivers/cxl/mem.c index fcffe24dcb42..ab88eaa31d1d 100644 --- a/drivers/cxl/mem.c +++ b/drivers/cxl/mem.c @@ -48,6 +48,11 @@ static int cxl_mem_dpa_show(struct seq_file *file, void *data) static int cxl_debugfs_poison_inject(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_inject_poison(cxlmd, dpa); } @@ -58,6 +63,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(cxl_poison_inject_fops, NULL, static int cxl_debugfs_poison_clear(void *data, u64 dpa) { struct cxl_memdev *cxlmd = data; + int rc; + + ACQUIRE(device_intr, devlock)(&cxlmd->dev); + if ((rc = ACQUIRE_ERR(device_intr, &devlock))) + return rc; return cxl_clear_poison(cxlmd, dpa); } From 16329b510f76e5b824e05bf8add8b29850f1f16f Mon Sep 17 00:00:00 2001 From: Koba Ko Date: Tue, 14 Apr 2026 10:45:27 +0800 Subject: [PATCH 02/18] cxl/region: Validate partition index before array access construct_region() reads cxled->part and uses it to index cxlds->part[] without checking for a negative value. If the partition was never resolved, part remains at its initial value of -1, causing an out-of-bounds array access. Add a guard to return -EBUSY when part is negative. The check was dropped during a merge. Signed-off-by: Koba Ko Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260414024527.3399590-1-kobak@nvidia.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e50dc716d4e8..cc41c08c0c0c 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -3714,6 +3714,9 @@ static struct cxl_region *construct_region(struct cxl_root_decoder *cxlrd, int rc, part = READ_ONCE(cxled->part); struct cxl_region *cxlr; + if (part < 0) + return ERR_PTR(-EBUSY); + do { cxlr = __create_region(cxlrd, cxlds->part[part].mode, atomic_read(&cxlrd->region_id), From d40745cd06f35095a7b2925ea3217bf7ef764832 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Wed, 27 May 2026 17:03:32 +0800 Subject: [PATCH 03/18] cxl/test: Enforce PMD alignment for volatile mock regions cxl_test allocates synthetic CFMWS HPA windows from a gen_pool with SZ_256M alignment. On arm64 with CONFIG_ARM64_64K_PAGES=y and CONFIG_PGTABLE_LEVELS=3, PMD_SIZE is 512M, so every CXL region carved from a volatile window inherits a non-PMD-aligned start, and cxl_dax_region_probe() -> alloc_dax_region() fails: """ cxl_dax_region dax_region1: probe with driver cxl_dax_region failed with error -12 """ Enforce that every volatile mock CFMWS is PMD-aligned in both start and size Reviewed-by: Dave Jiang Acked-by: Kai-Heng Feng Signed-off-by: Richard Cheng Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260527090332.30002-1-icheng@nvidia.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 296516eecfd6..4281d34cd0e7 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -318,7 +318,7 @@ static struct { .restrictions = ACPI_CEDT_CFMWS_RESTRICT_HOSTONLYMEM | ACPI_CEDT_CFMWS_RESTRICT_VOLATILE, .qtg_id = FAKE_QTG_ID, - .window_size = SZ_256M, + .window_size = SZ_256M > PMD_SIZE ? SZ_256M : PMD_SIZE, }, .target = { 3 }, }, @@ -495,9 +495,12 @@ static int populate_cedt(void) for (i = cfmws_start; i <= cfmws_end; i++) { struct acpi_cedt_cfmws *window = mock_cfmws[i]; + int align = SZ_256M; cfmws_elc_update(window, i); - res = alloc_mock_res(window->window_size, SZ_256M); + if (window->restrictions & ACPI_CEDT_CFMWS_RESTRICT_VOLATILE) + align = max_t(int, SZ_256M, PMD_SIZE); + res = alloc_mock_res(window->window_size, align); if (!res) return -ENOMEM; window->base_hpa = res->range.start; @@ -1819,6 +1822,12 @@ static __init int cxl_test_init(void) int rc, i; struct range mappable; + if (!IS_ALIGNED(mock_auto_region_size, PMD_SIZE)) { + pr_err_once("mock_auto_region_size %d must be PMD-aligned\n", + mock_auto_region_size); + return -EINVAL; + } + cxl_acpi_test(); cxl_core_test(); cxl_mem_test(); From 7d178454d0fb555f88d5732a76adf47390ae629d Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 26 May 2026 17:13:03 -0700 Subject: [PATCH 04/18] MAINTAINERS: Add CXL reviewer Add Li Ming as CXL subsystem reviewer. Thanks to Li Ming for all the CXL bugs they've found and fixed, and looking forward to many more prevented! Signed-off-by: Alison Schofield Acked-by: Jonathan Cameron Reviewed-by: Li Ming Acked-by: Dave Jiang Link: https://patch.msgid.link/20260527001305.533170-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9ec290e38b44..635b056a20c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6437,6 +6437,7 @@ M: Alison Schofield M: Vishal Verma M: Ira Weiny M: Dan Williams +R: Li Ming L: linux-cxl@vger.kernel.org S: Maintained F: Documentation/driver-api/cxl From 6c9d2e87df40d606f1c85143e9acb1ecff463d5e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:03 -0700 Subject: [PATCH 05/18] cxl/fwctl: Fix __fortify_panic Fix a runtime assertion in cxlctl_get_supported_features(). Fortify complains that it is potentially overflowing the entries array per __counted_by_le(num_entries). Quiet the false positive by initializing @num_entries earlier. memcpy: detected buffer overflow: 48 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#7: fwctl/1398 RIP: 0010:__fortify_report+0x50/0xa0 Call Trace: __fortify_panic+0xd/0xf cxlctl_get_supported_features.cold+0x23/0x35 [cxl_core] Fixes: 4d1c09cef2c2 ("cxl: Add support for fwctl RPC command to enable CXL feature commands") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-2-djbw@kernel.org Signed-off-by: Dave Jiang --- drivers/cxl/core/features.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c index 3435db9ea6b1..85185af46b72 100644 --- a/drivers/cxl/core/features.c +++ b/drivers/cxl/core/features.c @@ -423,6 +423,7 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, rpc_out->size = struct_size(feat_out, ents, requested); feat_out = &rpc_out->get_sup_feats_out; + feat_out->num_entries = cpu_to_le16(requested); for (i = start, pos = &feat_out->ents[0]; i < cxlfs->entries->num_features; i++, pos++) { @@ -444,7 +445,6 @@ static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, } } - feat_out->num_entries = cpu_to_le16(requested); feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; *out_len = out_size; From 08326b92c7a414a73b5b308d1daf0e91e0134dfc Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 19 May 2026 15:12:04 -0700 Subject: [PATCH 06/18] cxl/test: Fix __fortify_panic Fix a runtime assertion in setup_xor_mapping(). Fortify complains that it is potentially overflowing the xormaps array per __counted_by(nr_maps). Quiet the false positive by initializing @nr_maps earlier. memcpy: detected buffer overflow: 32 byte write of buffer size 0 WARNING: lib/string_helpers.c:1036 at __fortify_report+0x4d/0xa0, CPU#8: modprobe/2728 Call Trace: __fortify_panic+0xd/0xf setup_xor_mapping+0x6c/0xa0 [cxl_translate] [ dj: Fixed up @nr_entries to @nr_maps in commit log. ] Fixes: 06377c54a133 ("cxl/test: Add cxl_translate module for address translation testing") Signed-off-by: Dan Williams Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260519221204.1517773-3-djbw@kernel.org Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl_translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl_translate.c b/tools/testing/cxl/test/cxl_translate.c index 16328b2112b2..25a27e01ac21 100644 --- a/tools/testing/cxl/test/cxl_translate.c +++ b/tools/testing/cxl/test/cxl_translate.c @@ -236,8 +236,8 @@ static int setup_xor_mapping(void) if (!cximsd) return -ENOMEM; - memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); cximsd->nr_maps = nr_maps; + memcpy(cximsd->xormaps, xormaps, nr_maps * sizeof(*cximsd->xormaps)); return 0; } From c268f949e219f9e179558e836f457f6c5fbec416 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Fri, 5 Jun 2026 13:06:10 -0500 Subject: [PATCH 07/18] cxl: Fix CXL_HEADERLOG_SIZE to match RAS Capability size The CXL r4.0 8.2.4.17.7 RAS Capability Structure has total length 0x58 bytes (CXL_RAS_CAPABILITY_LENGTH); the Header Log occupies the trailing 64 bytes at offset 0x18. CXL_HEADERLOG_SIZE was defined as SZ_512, eight times the actual on-device size. header_log_copy() reads CXL_HEADERLOG_SIZE_U32 (128) dwords from the RAS capability iomap, overrunning the 88-byte mapping by 448 bytes. The cxl_aer_uncorrectable_error trace event memcpy()s CXL_HEADERLOG_SIZE (512) bytes from its source. For the CPER caller the source is struct cxl_ras_capability_regs::header_log[16] (64 bytes) embedded in a stack-local cxl_cper_prot_err_work_data, so the memcpy reads 448 bytes of kernel stack into the trace event ring buffer where userspace can read it via tracefs. Set CXL_HEADERLOG_SIZE to 64 and derive CXL_HEADERLOG_SIZE_U32 from it, bringing all iomap readers into agreement on 16 dwords. Userspace tools such as rasdaemon have grown a dependency on the buggy 512-byte (128 u32) header_log layout in the cxl_aer_uncorrectable_error trace event. Add CXL_HEADERLOG_TRACE_SIZE_U32 = 128 and use it for the trace event __array and its memcpy to preserve that ABI. Both callers now pass a zero-filled u32[CXL_HEADERLOG_TRACE_SIZE_U32] staging buffer with only the first CXL_HEADERLOG_SIZE_U32 (16) entries populated from hardware; the remaining 112 u32s are zero-padded, keeping the 512-byte trace ring buffer layout intact. [ dj: Replaced 64 with SZ_64 per RichardC ] Fixes: 36f257e3b0ba ("acpi/ghes, cxl/pci: Process CXL CPER Protocol Errors") Fixes: 2905cb5236cb ("cxl/pci: Add (hopeful) error handling support") Cc: stable@vger.kernel.org Reported-by: Sashiko Signed-off-by: Terry Bowman Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Reviewed-by: Ben Cheatham Reviewed-by: Richard Cheng Link: https://patch.msgid.link/20260605180610.2249458-1-terry.bowman@amd.com Signed-off-by: Dave Jiang --- drivers/cxl/core/ras.c | 27 ++++++++++++++++++++------- drivers/cxl/core/trace.h | 24 ++++++++++++++++-------- drivers/cxl/cxl.h | 14 ++++++++++++-- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 006c6ffc2f56..99fb00949c2f 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -8,6 +8,10 @@ #include #include "trace.h" +/* Check that UCE header definition is maintained to keep ABI intact */ +static_assert(CXL_HEADERLOG_TRACE_SIZE_U32 == 128, + "rasdaemon ABI requires exactly 128 u32s"); + static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { @@ -19,6 +23,7 @@ static void cxl_cper_trace_corr_port_prot_err(struct pci_dev *pdev, static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -28,8 +33,8 @@ static void cxl_cper_trace_uncorr_port_prot_err(struct pci_dev *pdev, else fe = status; - trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, - ras_cap.header_log); + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_port_aer_uncorrectable_error(&pdev->dev, status, fe, hl); } static void cxl_cper_trace_corr_prot_err(struct cxl_memdev *cxlmd, @@ -44,6 +49,7 @@ static void cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, struct cxl_ras_capability_regs ras_cap) { + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; u32 status = ras_cap.uncor_status & ~ras_cap.uncor_mask; u32 fe; @@ -53,8 +59,15 @@ cxl_cper_trace_uncorr_prot_err(struct cxl_memdev *cxlmd, else fe = status; - trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, - ras_cap.header_log); + /* + * ras_cap.header_log[] holds CXL_HEADERLOG_SIZE_U32 (16) hardware + * dwords. Copy them into the front of a zero-filled + * CXL_HEADERLOG_TRACE_SIZE_U32 (128) u32 staging buffer so the trace + * event memcpy sees a full 512-byte source and the userspace ABI + * (rasdaemon) is preserved. + */ + memcpy(hl, ras_cap.header_log, CXL_HEADERLOG_SIZE); + trace_cxl_aer_uncorrectable_error(cxlmd, status, fe, hl); } static int match_memdev_by_parent(struct device *dev, const void *uport) @@ -204,12 +217,12 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) { void __iomem *addr; u32 *log_addr; - int i, log_u32_size = CXL_HEADERLOG_SIZE / sizeof(u32); + int i; addr = ras_base + CXL_RAS_HEADER_LOG_OFFSET; log_addr = log; - for (i = 0; i < log_u32_size; i++) { + for (i = 0; i < CXL_HEADERLOG_SIZE_U32; i++) { *log_addr = readl(addr); log_addr++; addr += sizeof(u32); @@ -222,7 +235,7 @@ static void header_log_copy(void __iomem *ras_base, u32 *log) */ bool cxl_handle_ras(struct device *dev, void __iomem *ras_base) { - u32 hl[CXL_HEADERLOG_SIZE_U32]; + u32 hl[CXL_HEADERLOG_TRACE_SIZE_U32] = {}; void __iomem *addr; u32 status; u32 fe; diff --git a/drivers/cxl/core/trace.h b/drivers/cxl/core/trace.h index a972e4ef1936..d37876096dd7 100644 --- a/drivers/cxl/core/trace.h +++ b/drivers/cxl/core/trace.h @@ -56,7 +56,7 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __string(host, dev_name(dev->parent)) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(device); @@ -64,10 +64,14 @@ TRACE_EVENT(cxl_port_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("device=%s host=%s status: '%s' first_error: '%s'", __get_str(device), __get_str(host), @@ -85,7 +89,7 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __field(u64, serial) __field(u32, status) __field(u32, first_error) - __array(u32, header_log, CXL_HEADERLOG_SIZE_U32) + __array(u32, header_log, CXL_HEADERLOG_TRACE_SIZE_U32) ), TP_fast_assign( __assign_str(memdev); @@ -94,10 +98,14 @@ TRACE_EVENT(cxl_aer_uncorrectable_error, __entry->status = status; __entry->first_error = fe; /* - * Embed the 512B headerlog data for user app retrieval and - * parsing, but no need to print this in the trace buffer. + * Embed headerlog data for user app retrieval and parsing, + * but no need to print in the trace buffer. Only + * CXL_HEADERLOG_SIZE_U32 (16) dwords are hardware data; + * the remaining entries preserve the 512-byte ABI layout + * rasdaemon depends on and are zero-filled by the caller. */ - memcpy(__entry->header_log, hl, CXL_HEADERLOG_SIZE); + memcpy(__entry->header_log, hl, + CXL_HEADERLOG_TRACE_SIZE_U32 * sizeof(u32)); ), TP_printk("memdev=%s host=%s serial=%lld: status: '%s' first_error: '%s'", __get_str(memdev), __get_str(host), __entry->serial, diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 1297594beaec..21fc89d3aeea 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -158,8 +158,18 @@ static inline int ways_to_eiw(unsigned int ways, u8 *eiw) #define CXL_RAS_CAP_CONTROL_FE_MASK GENMASK(5, 0) #define CXL_RAS_HEADER_LOG_OFFSET 0x18 #define CXL_RAS_CAPABILITY_LENGTH 0x58 -#define CXL_HEADERLOG_SIZE SZ_512 -#define CXL_HEADERLOG_SIZE_U32 SZ_512 / sizeof(u32) +#define CXL_HEADERLOG_SIZE SZ_64 +#define CXL_HEADERLOG_SIZE_U32 (CXL_HEADERLOG_SIZE / sizeof(u32)) + +/* + * The RAS UCE trace event header array was originally sized at SZ_512/sizeof(u32) + * = 128 u32s due to a bug. Userspace tools (rasdaemon) have grown a dependency + * on that 512-byte layout. Keep the trace array at 128 u32s to preserve the + * ABI; only CXL_HEADERLOG_SIZE_U32 (16) dwords are valid hardware data, the + * remainder are zero-filled. + */ +#define CXL_HEADERLOG_TRACE_SIZE SZ_512 +#define CXL_HEADERLOG_TRACE_SIZE_U32 (CXL_HEADERLOG_TRACE_SIZE / sizeof(u32)) /* CXL 2.0 8.2.8.1 Device Capabilities Array Register */ #define CXLDEV_CAP_ARRAY_OFFSET 0x0 From 1e1edc973c64307821ee22049908e7ded8f973c2 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 4 Jun 2026 21:05:01 -0700 Subject: [PATCH 08/18] cxl/region: Avoid variable shadowing in region attach paths A couple of symbol declarations shadow earlier variables in the region attach paths. Shadowing makes it harder to tell which object is being referenced and can obscure future bugs. Reuse the existing 'cxld' variable in cxl_port_attach_region() and rename the endpoint decoder iterator in cxl_region_attach() to avoid shadowing the function parameter. No functional change. Found with sparse. Signed-off-by: Alison Schofield Reviewed-by: Li Ming Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260605040504.865728-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index cc41c08c0c0c..f5cd20f48d2b 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1224,8 +1224,6 @@ static int cxl_port_attach_region(struct cxl_port *port, nr_targets_inc = true; } } else { - struct cxl_decoder *cxld; - cxld = cxl_port_pick_region_decoder(port, cxled, cxlr); if (!cxld) { dev_dbg(&cxlr->dev, "%s: no decoder available\n", @@ -2189,14 +2187,14 @@ static int cxl_region_attach(struct cxl_region *cxlr, * will fail when presented as CXL_REGION_F_AUTO. */ for (int i = 0; i < p->nr_targets; i++) { - struct cxl_endpoint_decoder *cxled = p->targets[i]; + struct cxl_endpoint_decoder *target = p->targets[i]; int test_pos; - test_pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); - dev_dbg(&cxled->cxld.dev, - "Test cxl_calc_interleave_pos(): %s test_pos:%d cxled->pos:%d\n", - (test_pos == cxled->pos) ? "success" : "fail", - test_pos, cxled->pos); + test_pos = cxl_calc_interleave_pos(target, &cxlr->hpa_range); + dev_dbg(&target->cxld.dev, + "Test cxl_calc_interleave_pos(): %s test_pos:%d target->pos:%d\n", + (test_pos == target->pos) ? "success" : "fail", + test_pos, target->pos); } return 0; From 0e7041eb96fa9c07ec57dd8b4088f03b7939cdd3 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Tue, 9 Jun 2026 21:21:01 -0700 Subject: [PATCH 09/18] cxl/port: update reference to removed CONFIG_PROVE_CXL_LOCKING A comment in drivers/cxl/port.c refers to CONFIG_PROVE_CXL_LOCKING, which was removed in commit 38a34e10768c ("cxl: Drop cxl_device_lock()"). That commit switched CXL subsystem locking to custom lock classes, which can be validated via the standard CONFIG_PROVE_LOCKING option. Update the comment to reflect this. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Reviewed-by: Dan Williams Reviewed-by: Richard Cheng Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260610042101.222349-1-enelsonmoore@gmail.com Signed-off-by: Dave Jiang --- drivers/cxl/port.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c index ada51948d52f..99cf77b6b699 100644 --- a/drivers/cxl/port.c +++ b/drivers/cxl/port.c @@ -18,7 +18,7 @@ * firmware) are managed in this drivers context. Each driver instance * is responsible for tearing down the driver context of immediate * descendant ports. The locking for this is validated by - * CONFIG_PROVE_CXL_LOCKING. + * CONFIG_PROVE_LOCKING. * * The primary service this driver provides is presenting APIs to other * drivers to utilize the decoders, and indicating to userspace (via bind From 71a1def165267bc0947d4236f7336f490739c379 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:15:08 -0700 Subject: [PATCH 10/18] cxl/test: Verify cmd->size_in before accessing payload Several mock mailbox handlers access input payload fields before verifying that cmd->size_in is large enough for the corresponding structure. To ensure invalid commands are rejected before any payload data is consumed, add missing size checks and move existing checks ahead of the first payload field access. [dj: Updated commit log per Alison's comments. ] Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Fixes: d1dca858f058 ("cxl/test: Add generic mock events") Fixes: f6448cb5f2f3 ("tools/testing/cxl: add firmware update emulation to CXL memdevs") Fixes: e77e9c107978 ("cxl/test: Add Get Feature support to cxl_test") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Suggested-by: sashiko-bot Tested-by: Alison Schofield Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 39 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 271c7ad8cc32..2e9a5f151e98 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -312,12 +312,17 @@ static int mock_get_event(struct device *dev, struct cxl_mbox_cmd *cmd) static int mock_clear_event(struct device *dev, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_clear_event_payload *pl = cmd->payload_in; + struct cxl_mbox_clear_event_payload *pl; struct mock_event_log *log; - u8 log_type = pl->event_log; + u8 log_type; u16 handle; int nr; + if (cmd->size_in < sizeof(*pl)) + return -EINVAL; + + pl = cmd->payload_in; + log_type = pl->event_log; if (log_type >= CXL_EVENT_TYPE_MAX) return -EINVAL; @@ -574,14 +579,19 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd) static int mock_get_log(struct cxl_memdev_state *mds, struct cxl_mbox_cmd *cmd) { struct cxl_mailbox *cxl_mbox = &mds->cxlds.cxl_mbox; - struct cxl_mbox_get_log *gl = cmd->payload_in; - u32 offset = le32_to_cpu(gl->offset); - u32 length = le32_to_cpu(gl->length); uuid_t uuid = DEFINE_CXL_CEL_UUID; + struct cxl_mbox_get_log *gl; void *data = &mock_cel; + u32 offset; + u32 length; if (cmd->size_in < sizeof(*gl)) return -EINVAL; + + gl = cmd->payload_in; + offset = le32_to_cpu(gl->offset); + length = le32_to_cpu(gl->length); + if (length > cxl_mbox->payload_size) return -EINVAL; if (offset + length > sizeof(mock_cel)) @@ -1336,10 +1346,14 @@ static int mock_fw_info(struct cxl_mockmem_data *mdata, static int mock_transfer_fw(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { - struct cxl_mbox_transfer_fw *transfer = cmd->payload_in; + struct cxl_mbox_transfer_fw *transfer; void *fw = mdata->fw; size_t offset, length; + if (cmd->size_in < sizeof(*transfer)) + return -EINVAL; + + transfer = cmd->payload_in; offset = le32_to_cpu(transfer->offset) * CXL_FW_TRANSFER_ALIGNMENT; length = cmd->size_in - sizeof(*transfer); if (offset + length > FW_SIZE) @@ -1415,11 +1429,18 @@ static int mock_get_test_feature(struct cxl_mockmem_data *mdata, struct cxl_mbox_cmd *cmd) { struct vendor_test_feat *output = cmd->payload_out; - struct cxl_mbox_get_feat_in *input = cmd->payload_in; - u16 offset = le16_to_cpu(input->offset); - u16 count = le16_to_cpu(input->count); + struct cxl_mbox_get_feat_in *input; + u16 offset; + u16 count; u8 *ptr; + if (cmd->size_in < sizeof(*input)) + return -EINVAL; + + input = cmd->payload_in; + offset = le16_to_cpu(input->offset); + count = le16_to_cpu(input->count); + if (offset > sizeof(*output)) { cmd->return_code = CXL_MBOX_CMD_RC_INPUT; return -EINVAL; From 81eafcada109b653977c4dfbd2b6a72470025a01 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 10:12:38 -0700 Subject: [PATCH 11/18] cxl/test: Fix integer overflow in mock LSA bounds checks Pre-existing issue discovered by sashiko-bot. mock_get_lsa() and mock_set_lsa() validate the requested LSA range with "offset + length > LSA_SIZE". Both offset and length are u32 and, in mock_get_lsa(), both are taken directly from the user-supplied payload. The addition is evaluated modulo 2^32, so a large offset combined with a small length wraps around and passes the check. Rewrite the checks to first bound offset, then compare length against the remaining LSA size. Suggested-by: sashiko-bot Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Link: https://lore.kernel.org/linux-cxl/20260605143748.235271F00893@smtp.kernel.org/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Alison Schofield Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 2e9a5f151e98..9a7cd3f46a1e 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1063,7 +1063,7 @@ static int mock_get_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(get_lsa->offset); length = le32_to_cpu(get_lsa->length); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; if (length > cmd->size_out) return -EINVAL; @@ -1083,7 +1083,7 @@ static int mock_set_lsa(struct cxl_mockmem_data *mdata, return -EINVAL; offset = le32_to_cpu(set_lsa->offset); length = cmd->size_in - sizeof(*set_lsa); - if (offset + length > LSA_SIZE) + if (offset > LSA_SIZE || length > LSA_SIZE - offset) return -EINVAL; memcpy(lsa + offset, &set_lsa->data[0], length); From 60f065dbaf46e65830da62a0041761f0c039e086 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Fri, 5 Jun 2026 11:44:26 -0700 Subject: [PATCH 12/18] cxl/test: Zero out LSA backing memory to avoid leaking to user Memory through vmalloc() is not zeroed out. When this memory is copied into output payload, it leaks memory content to user. Use vzalloc() instead to zero out the memory. Suggested-by: sashiko-bot Link: https://lore.kernel.org/linux-cxl/20260605173146.2B9A31F00893@smtp.kernel.org/ Fixes: 7d3eb23c4ccf ("tools/testing/cxl: Introduce a mock memory device + driver") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260605184426.4070913-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c index 9a7cd3f46a1e..739343cd5802 100644 --- a/tools/testing/cxl/test/mem.c +++ b/tools/testing/cxl/test/mem.c @@ -1724,7 +1724,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev) return -ENOMEM; dev_set_drvdata(dev, mdata); - mdata->lsa = vmalloc(LSA_SIZE); + mdata->lsa = vzalloc(LSA_SIZE); if (!mdata->lsa) return -ENOMEM; mdata->fw = vmalloc(FW_SIZE); From 50cc34be04a0ea7522b739c9c7a71367cfbc489c Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:55 -0700 Subject: [PATCH 13/18] cxl/test: Unregister cxl_acpi in cxl_test_init() error path In cxl_test_init(), Once cxl_mock_platform_device_add() succeeds, all error paths after needs to call platform_device_unregister() instead of platform_device_put() to clean up. Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reported-by: sashiko-bot Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230355.198912-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 4281d34cd0e7..7351fb87c7ab 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1960,7 +1960,7 @@ static __init int cxl_test_init(void) err_mem: cxl_mem_exit(); err_root: - platform_device_put(cxl_acpi); + platform_device_unregister(cxl_acpi); err_rch: cxl_rch_topo_exit(); err_single: From dfe28c8592538152e9611341dae6f7be1735b3f1 Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Thu, 11 Jun 2026 16:03:05 -0700 Subject: [PATCH 14/18] cxl/test: Add check after kzalloc() memory in alloc_mock_res() alloc_mock_res() calls kzalloc() without checking the return value. Add scope based resource management to deal with the allocated memory cleanly. Reported-by: sashiko-bot Fixes: 67dcdd4d3b83 ("tools/testing/cxl: Introduce a mocked-up CXL port hierarchy") Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260611230305.197390-1-dave.jiang@intel.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 7351fb87c7ab..9a0faf70e9b1 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -433,12 +433,16 @@ static void depopulate_all_mock_resources(void) static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) { - struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL); struct genpool_data_align data = { .align = align, }; unsigned long phys; + struct cxl_mock_res *res __free(kfree) = kzalloc(sizeof(*res), + GFP_KERNEL); + if (!res) + return NULL; + INIT_LIST_HEAD(&res->list); phys = gen_pool_alloc_algo(cxl_mock_pool, size, gen_pool_first_fit_align, &data); @@ -453,7 +457,7 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size, int align) list_add(&res->list, &mock_res); mutex_unlock(&mock_res_lock); - return res; + return no_free_ptr(res); } /* Only update CFMWS0 as this is used by the auto region. */ From 661c092f983975842da8fa6281e4a1a70f357699 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 4 Jun 2026 21:07:59 -0700 Subject: [PATCH 15/18] cxl: Align interleave decode/encode helpers with their callers The interleave conversion helpers translate between encoded HDM interleave values and the granularity and way values used by the driver. These helpers have been a recurring source of static analysis complaints that expose type mismatches and potentially uninitialized outputs. Fix those issues in the helpers so callers inherit the consistent behavior automatically. The decode and encode helpers have different interface issues. The decode helpers return values through unsigned int pointers, but the decoded values are ultimately represented as int throughout the driver. Align the helper interfaces with their callers by changing the out-parameters to int * and updating the handful of affected locals to match. The encode helpers leave their out-parameters unchanged on error. That means callers that ignore the return value may observe uninitialized encoded values. Initialize the outputs so failed conversions leave defined values. This issue was originally reported by Purva and the helper-side fix was suggested by Dan [1]. Tidy up a related, pre-existing, printk format specifier mismatch in cxl_validate_translation_params(). No functional change for valid interleave parameters. [1] https://lore.kernel.org/linux-cxl/20250419203530.45594-1-purvayeshi550@gmail.com/ Reported-by: Purva Yeshi Suggested-by: Dan Williams Signed-off-by: Alison Schofield Reviewed-by: Li Ming Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260605040801.865965-1-alison.schofield@intel.com Signed-off-by: Dave Jiang --- drivers/cxl/acpi.c | 10 +++++----- drivers/cxl/core/region.c | 4 ++-- drivers/cxl/cxl.h | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index 127537628817..3b818adbd38b 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -101,8 +101,8 @@ static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg, struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld; struct device *dev = ctx->dev; struct cxl_cxims_data *cximsd; - unsigned int hbig, nr_maps; - int rc; + unsigned int nr_maps; + int hbig, rc; rc = eig_to_granularity(cxims->hbig, &hbig); if (rc) @@ -160,7 +160,7 @@ static int cxl_acpi_cfmws_verify(struct device *dev, struct acpi_cedt_cfmws *cfmws) { int rc, expected_len; - unsigned int ways; + int ways; if (cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_MODULO && cfmws->interleave_arithmetic != ACPI_CEDT_CFMWS_ARITHMETIC_XOR) { @@ -405,7 +405,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, struct cxl_cxims_context cxims_ctx; struct device *dev = ctx->dev; struct cxl_decoder *cxld; - unsigned int ways, i, ig; + int ways, i, ig; int rc; rc = cxl_acpi_cfmws_verify(dev, cfmws); @@ -464,7 +464,7 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws, if (rc < 0) return rc; if (!cxlrd->platform_data) { - dev_err(dev, "No CXIMS for HBIG %u\n", ig); + dev_err(dev, "No CXIMS for HBIG %d\n", ig); return -EINVAL; } } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index f5cd20f48d2b..1d4d3b005178 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -3058,7 +3058,7 @@ int cxl_validate_translation_params(u8 eiw, u16 eig, int pos) return -EINVAL; } if (pos < 0 || pos >= ways) { - pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos, + pr_debug("%s: invalid pos=%d for ways=%d\n", __func__, pos, ways); return -EINVAL; } @@ -3104,7 +3104,7 @@ EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate"); int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig) { - unsigned int ways = 0; + int ways = 0; u64 shifted, rem; int pos, ret; diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h index 21fc89d3aeea..4a884821ff7c 100644 --- a/drivers/cxl/cxl.h +++ b/drivers/cxl/cxl.h @@ -91,7 +91,7 @@ static inline int cxl_hdm_decoder_count(u32 cap_hdr) } /* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */ -static inline int eig_to_granularity(u16 eig, unsigned int *granularity) +static inline int eig_to_granularity(u16 eig, int *granularity) { if (eig > CXL_DECODER_MAX_ENCODED_IG) return -EINVAL; @@ -100,7 +100,7 @@ static inline int eig_to_granularity(u16 eig, unsigned int *granularity) } /* Encode defined in CXL ECN "3, 6, 12 and 16-way memory Interleaving" */ -static inline int eiw_to_ways(u8 eiw, unsigned int *ways) +static inline int eiw_to_ways(u8 eiw, int *ways) { switch (eiw) { case 0 ... 4: @@ -118,6 +118,7 @@ static inline int eiw_to_ways(u8 eiw, unsigned int *ways) static inline int granularity_to_eig(int granularity, u16 *eig) { + *eig = 0; if (granularity > SZ_16K || granularity < CXL_DECODER_MIN_GRANULARITY || !is_power_of_2(granularity)) return -EINVAL; @@ -127,6 +128,7 @@ static inline int granularity_to_eig(int granularity, u16 *eig) static inline int ways_to_eiw(unsigned int ways, u8 *eiw) { + *eiw = 0; if (ways > 16) return -EINVAL; if (is_power_of_2(ways)) { From 769f0b350c81ab147fff37b92637e12190f1be29 Mon Sep 17 00:00:00 2001 From: Richard Cheng Date: Fri, 12 Jun 2026 09:12:27 +0800 Subject: [PATCH 16/18] tools/testing/cxl: Resolve auto-region decoder targets like real HW The mock auto-region created at module load wrote switch and host-bridge decoder target[] directly, in addition to target_map[]. Real HW programs only target_map[] and resolves target[] as dports enumerate, via update_decoder_targets(). Region replay already follows that ordering, the initial auto-region did not. Drop the direct target[] writes and call cxl_port_update_decoder_targets() so target[] is resolved the same way as real HW and region replay, exercising more of the auto-region driver path. This is inspired by the discussion [1] below: [1]: https://lore.kernel.org/all/20260521084806.28232-1-icheng@nvidia.com/ Suggested-by: Alison Schofield Signed-off-by: Richard Cheng Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Tested-by: Dave Jiang Link: https://patch.msgid.link/20260612011227.4220-1-icheng@nvidia.com Signed-off-by: Dave Jiang --- tools/testing/cxl/test/cxl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 9a0faf70e9b1..ef92dd35e030 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1188,15 +1188,11 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxlsd = to_cxl_switch_decoder(dev); if (i == 0) { /* put cxl_mem.4 second in the decode order */ - if (pdev->id == 4) { - cxlsd->target[1] = dport; + if (pdev->id == 4) cxlsd->cxld.target_map[1] = dport->port_id; - } else { - cxlsd->target[0] = dport; + else cxlsd->cxld.target_map[0] = dport->port_id; - } } else { - cxlsd->target[0] = dport; cxlsd->cxld.target_map[0] = dport->port_id; } cxld = &cxlsd->cxld; @@ -1219,6 +1215,16 @@ static bool mock_init_hdm_decoder(struct cxl_decoder *cxld) cxld->commit = mock_decoder_commit; cxld->reset = mock_decoder_reset; + /* + * Only target_map[] is programmed above, mimicking + * firmware. On real hardware target[] is populated as + * dports enumerate, via update_decoder_targets(). The + * mock's dports are already bound by now, so fire that + * resolution explicitly here rather than stamping + * target[] directly. + */ + cxl_port_update_decoder_targets(iter, dport); + cxld_registry_update(cxld); put_device(dev); } From cbda6a2c2bec2a5fb30a2ce85baeab15b5fc7db3 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:00 +0800 Subject: [PATCH 17/18] cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach() In cxl_cancel_auto_attach(), it assumes cxled->pos is a valid index for accessing p->targets[]. However, cxled->pos can be set to negative errno in cxl_region_sort_targets() if cxl_calc_interleave_pos() fails. This causes the driver to use a negative index to access p->targets[], resulting in out-of-bounds access. Fix it by walking p->targets[] instead of using cxled->pos directly. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-1-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 40 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 1d4d3b005178..690ae991a80f 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -2009,8 +2009,9 @@ static int cxl_region_sort_targets(struct cxl_region *cxlr) cxled->pos = cxl_calc_interleave_pos(cxled, &cxlr->hpa_range); /* * Record that sorting failed, but still continue to calc - * cxled->pos so that follow-on code paths can reliably - * do p->targets[cxled->pos] to self-reference their entry. + * cxled->pos so that cxl_calc_interleave_pos() emits its + * dev_dbg() for every member. which is useful for auto + * discovery debug. */ if (cxled->pos < 0) rc = -ENXIO; @@ -2200,18 +2201,30 @@ static int cxl_region_attach(struct cxl_region *cxlr, return 0; } -static int cxl_region_by_target(struct device *dev, const void *data) +static int cxl_region_remove_target(struct device *dev, void *data) { - const struct cxl_endpoint_decoder *cxled = data; + struct cxl_endpoint_decoder *cxled = data; struct cxl_region_params *p; struct cxl_region *cxlr; + int i; if (!is_cxl_region(dev)) return 0; cxlr = to_cxl_region(dev); p = &cxlr->params; - return p->targets[cxled->pos] == cxled; + for (i = 0; i < p->interleave_ways; i++) { + if (p->targets[i] == cxled) { + p->nr_targets--; + cxled->state = CXL_DECODER_STATE_AUTO; + cxled->pos = -1; + p->targets[i] = NULL; + + return 1; + } + } + + return 0; } /* @@ -2220,25 +2233,10 @@ static int cxl_region_by_target(struct device *dev, const void *data) */ static void cxl_cancel_auto_attach(struct cxl_endpoint_decoder *cxled) { - struct cxl_region_params *p; - struct cxl_region *cxlr; - int pos = cxled->pos; - if (cxled->state != CXL_DECODER_STATE_AUTO_STAGED) return; - struct device *dev __free(put_device) = - bus_find_device(&cxl_bus_type, NULL, cxled, cxl_region_by_target); - if (!dev) - return; - - cxlr = to_cxl_region(dev); - p = &cxlr->params; - - p->nr_targets--; - cxled->state = CXL_DECODER_STATE_AUTO; - cxled->pos = -1; - p->targets[pos] = NULL; + bus_for_each_dev(&cxl_bus_type, NULL, cxled, cxl_region_remove_target); } static struct cxl_region * From aa8a76711c15041ec1e42c3a74c15c2df0bd31f6 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Sat, 6 Jun 2026 15:51:01 +0800 Subject: [PATCH 18/18] cxl/region: Fill first free targets[] slot during auto-discovery Any invalid endpoint decoder pointer in the target array of an active region is not allowed by cxl driver. This means cxl driver always assumes the first p->nr_targets entries of the target array in an auto-assembly region are valid. However, there are scenarios that could leave NULL endpoint decoder pointer holes in the target array. 1. When cxl_cancel_auto_attach() removes an endpoint decoder from a target array, the target slot is set to NULL. If the removed endpoint decoder is not the last element in the target array, the target array will contain a NULL hole. 2. When a auto-assembly region removes an assigned endpoint decoder, if the removed endpoint decoder is not the last element in the target array, always remains a NULL hole in the target array. When a NULL pointer hole exists in a region's target array, it introduces two potential problems: 1. Access an endpoint decoder via a NULL pointer. it always trigger calltrace like that. Oops: general protection fault, probably for non-canonical address 0xdffffc0000000008: 0000 [#1] SMP KASAN PTI RIP: 0010:cxl_calc_interleave_pos+0x26/0x810 [cxl_core] Call Trace: cxl_region_attach+0xc50/0x2140 [cxl_core] cxl_add_to_region+0x321/0x2330 [cxl_core] discover_region+0x92/0x150 [cxl_port] device_for_each_child+0xf3/0x170 cxl_port_probe+0x150/0x200 [cxl_port] cxl_bus_probe+0x4f/0xa0 [cxl_core] really_probe+0x1c8/0x960 __driver_probe_device+0x323/0x450 driver_probe_device+0x45/0x120 __device_attach_driver+0x15d/0x280 bus_for_each_drv+0x10f/0x190 2. Not having enough valid endpoint decoders attached to an auto-assembly region. if an auto-assembly region is created with lock flag or assigned endpoint decoder with lock flag, which means assigned endpoint decoder will not be reset during detaching, they could re-attach to the auto-assembly region again. But cxl region driver relies on p->nr_targets to verify whether the required number of endpoint decoders has been attached, and NULL endpoint decoder pointers are still counted in that case. To fix above issues, adjust cxl_region_attach_auto() logic to find the first free target slot for endpoint decoder attachment, this ensures NULL holes in the target array are filled, rather than adding new endpoint decoders at the tail of the target array. Fixes: 87805c32e6ad ("cxl/region: Fix use-after-free from auto assembly failure") Fixes: 2230c4bdc412 ("cxl: Add handling of locked CXL decoder") Suggested-by: Alison Schofield Signed-off-by: Li Ming Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260606-fix_two_issues_introduced_by_cxl_cancel_auto_attach-v1-2-5d94ca06c4e4@zohomail.com Signed-off-by: Dave Jiang --- drivers/cxl/core/region.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index 690ae991a80f..66c328d1c14e 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -1846,8 +1846,21 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr, * this means that userspace can view devices in the wrong position * before the region activates, and must be careful to understand when * it might be racing region autodiscovery. + * + * The endpoint decoder will be recorded into the first free slot of + * the target array. */ - pos = p->nr_targets; + for (pos = 0; pos < p->interleave_ways; pos++) { + if (!p->targets[pos]) + break; + } + + if (pos == p->interleave_ways) { + dev_err(&cxlr->dev, "%s: unable to find a free target slot\n", + dev_name(&cxled->cxld.dev)); + return -ENXIO; + } + p->targets[pos] = cxled; cxled->pos = pos; cxled->state = CXL_DECODER_STATE_AUTO_STAGED;