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: 87805c32e6 ("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>
This commit is contained in:
Li Ming 2026-06-06 15:51:00 +08:00 committed by Dave Jiang
parent 769f0b350c
commit cbda6a2c2b

View File

@ -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 *