summaryrefslogtreecommitdiff
path: root/drivers/cxl
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2026-03-26 22:28:13 -0700
committerDave Jiang <dave.jiang@intel.com>2026-04-01 08:12:12 -0700
commit87805c32e6ad7b5ce2d9f7f47e76081857a4a335 (patch)
tree519123280a5fec90ace5b144d3877eab4e857864 /drivers/cxl
parente4de6b910bf3645c224cd873d4e03ce3dd81fbe0 (diff)
downloadlwn-87805c32e6ad7b5ce2d9f7f47e76081857a4a335.tar.gz
lwn-87805c32e6ad7b5ce2d9f7f47e76081857a4a335.zip
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>
Diffstat (limited to 'drivers/cxl')
-rw-r--r--drivers/cxl/core/region.c54
-rw-r--r--drivers/cxl/cxl.h6
2 files changed, 57 insertions, 3 deletions
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index f7b20f60ac5c..b89442931277 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -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;
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 9b947286eb9b..30a31968f266 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -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,
};
/**