cxl/region: Fix use-after-free from auto assembly failure

The following crash signature results from region destruction while an
endpoint decoder is staged, but not fully attached.

[ dj: Moved bus_find_device( to next line. ]

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Link: https://patch.msgid.link/20260327052821.440749-2-dan.j.williams@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
This commit is contained in:
Dan Williams 2026-03-26 22:28:13 -07:00 committed by Dave Jiang
parent e4de6b910b
commit 87805c32e6
2 changed files with 57 additions and 3 deletions

View File

@ -1064,6 +1064,14 @@ static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr,
if (!cxld->region) {
cxld->region = cxlr;
/*
* Now that cxld->region is set the intermediate staging state
* can be cleared.
*/
if (cxld == &cxled->cxld &&
cxled->state == CXL_DECODER_STATE_AUTO_STAGED)
cxled->state = CXL_DECODER_STATE_AUTO;
get_device(&cxlr->dev);
}
@ -1805,6 +1813,7 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr,
pos = p->nr_targets;
p->targets[pos] = cxled;
cxled->pos = pos;
cxled->state = CXL_DECODER_STATE_AUTO_STAGED;
p->nr_targets++;
return 0;
@ -2154,6 +2163,47 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return 0;
}
static int cxl_region_by_target(struct device *dev, const void *data)
{
const struct cxl_endpoint_decoder *cxled = data;
struct cxl_region_params *p;
struct cxl_region *cxlr;
if (!is_cxl_region(dev))
return 0;
cxlr = to_cxl_region(dev);
p = &cxlr->params;
return p->targets[cxled->pos] == cxled;
}
/*
* When an auto-region fails to assemble the decoder may be listed as a target,
* but not fully attached.
*/
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;
}
static struct cxl_region *
__cxl_decoder_detach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos,
@ -2177,8 +2227,10 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
cxled = p->targets[pos];
} else {
cxlr = cxled->cxld.region;
if (!cxlr)
if (!cxlr) {
cxl_cancel_auto_attach(cxled);
return NULL;
}
p = &cxlr->params;
}

View File

@ -378,12 +378,14 @@ struct cxl_decoder {
};
/*
* Track whether this decoder is reserved for region autodiscovery, or
* free for userspace provisioning.
* Track whether this decoder is free for userspace provisioning, reserved for
* region autodiscovery, whether it is started connecting (awaiting other
* peers), or has completed auto assembly.
*/
enum cxl_decoder_state {
CXL_DECODER_STATE_MANUAL,
CXL_DECODER_STATE_AUTO,
CXL_DECODER_STATE_AUTO_STAGED,
};
/**