]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach()
authorLi Ming <ming.li@zohomail.com>
Sat, 6 Jun 2026 07:51:00 +0000 (15:51 +0800)
committerDave Jiang <dave.jiang@intel.com>
Fri, 12 Jun 2026 15:48:02 +0000 (08:48 -0700)
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: 87805c32e6ad ("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>
drivers/cxl/core/region.c

index 1d4d3b005178daf7b13a0c879af06e4e67efc791..690ae991a80fc4ca97dbba3c39c23e5c0813633a 100644 (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 *