summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Ming <ming.li@zohomail.com>2026-06-06 10:51:00 +0300
committerDave Jiang <dave.jiang@intel.com>2026-06-12 18:48:02 +0300
commitcbda6a2c2bec2a5fb30a2ce85baeab15b5fc7db3 (patch)
tree03a12d266e61759b4a2da81e159120dbbb6c4bbd
parent769f0b350c81ab147fff37b92637e12190f1be29 (diff)
downloadlinux-cbda6a2c2bec2a5fb30a2ce85baeab15b5fc7db3.tar.xz
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 <ming.li@zohomail.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> 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 <dave.jiang@intel.com>
-rw-r--r--drivers/cxl/core/region.c40
1 files 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 *