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) { if (!cxld->region) {
cxld->region = cxlr; 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); get_device(&cxlr->dev);
} }
@ -1805,6 +1813,7 @@ static int cxl_region_attach_auto(struct cxl_region *cxlr,
pos = p->nr_targets; pos = p->nr_targets;
p->targets[pos] = cxled; p->targets[pos] = cxled;
cxled->pos = pos; cxled->pos = pos;
cxled->state = CXL_DECODER_STATE_AUTO_STAGED;
p->nr_targets++; p->nr_targets++;
return 0; return 0;
@ -2154,6 +2163,47 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return 0; 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 * static struct cxl_region *
__cxl_decoder_detach(struct cxl_region *cxlr, __cxl_decoder_detach(struct cxl_region *cxlr,
struct cxl_endpoint_decoder *cxled, int pos, struct cxl_endpoint_decoder *cxled, int pos,
@ -2177,8 +2227,10 @@ __cxl_decoder_detach(struct cxl_region *cxlr,
cxled = p->targets[pos]; cxled = p->targets[pos];
} else { } else {
cxlr = cxled->cxld.region; cxlr = cxled->cxld.region;
if (!cxlr) if (!cxlr) {
cxl_cancel_auto_attach(cxled);
return NULL; return NULL;
}
p = &cxlr->params; p = &cxlr->params;
} }

View File

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