Re: [PATCH v3 1/1] cxl/region: Fix out-of-bounds access in cxl_cancel_auto_attach()

From: Alison Schofield

Date: Fri May 29 2026 - 11:43:10 EST


On Fri, May 29, 2026 at 09:25:33PM +0800, Li Ming wrote:
> 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@xxxxxxxxxxxx>


Reviewed-by: Alison Schofield <alison.schofield@xxxxxxxxx>


> ---
> Changes from v2:
> - Revert "Fix potential 'hole' issue." changes. (Alison)
>
> Changes from v1:
> - Update commit log. (Dave)
> - Update comment in cxl_region_sort_targets(). (Dave)
> - Fix potential 'hole' issue. (Alison & Sashiko review)
> ---
> drivers/cxl/core/region.c | 40 +++++++++++++++++++--------------------
> 1 file changed, 19 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
> index e50dc716d4e8..eb96cb3cae83 100644
> --- a/drivers/cxl/core/region.c
> +++ b/drivers/cxl/core/region.c
> @@ -2011,8 +2011,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;
> @@ -2202,18 +2203,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->nr_targets; 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;
> }
>
> /*
> @@ -2222,25 +2235,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 *
> --
> 2.43.0
>