]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cxl/region: Fix use-after-free from auto assembly failure
authorDan Williams <dan.j.williams@intel.com>
Fri, 27 Mar 2026 05:28:13 +0000 (22:28 -0700)
committerDave Jiang <dave.jiang@intel.com>
Wed, 1 Apr 2026 15:12:12 +0000 (08:12 -0700)
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>
drivers/cxl/core/region.c
drivers/cxl/cxl.h

index f7b20f60ac5cd9c099bd976b816f970ec6da4bdd..b8944293127731d487215620effbba1123ac2adf 100644 (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;
        }
 
index 9b947286eb9b013dc1f7753766f4e601faedb816..30a31968f26631b4bb90f1dbcec075b0d3600a5c 100644 (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,
 };
 
 /**