Netdev List
 help / color / mirror / Atom feed
From: Dave Jiang <dave.jiang@intel.com>
To: "Lucero Palau, Alejandro" <alejandro.lucero-palau@amd.com>,
	alucerop@amd.com, linux-cxl@vger.kernel.org,
	netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com,
	edumazet@google.com, ecree.xilinx@gmail.com, icheng@nvidia.com,
	rafael@kernel.org
Subject: Re: [PATCH v2 2/4] cxl/region: Add region reference in memdev attach
Date: Fri, 2 Oct 2026 08:52:46 -0700	[thread overview]
Message-ID: <bd412198-8a25-436c-b6d6-6b6730e8c28e@intel.com> (raw)
In-Reply-To: <8ebaae42-a0c6-4e9c-be1b-ca68a4769ed7@amd.com>

[-- Attachment #1: Type: text/plain, Size: 6892 bytes --]



On 10/1/26 9:41 PM, Lucero Palau, Alejandro wrote:
> 
> On 01/10/2026 22:38, Dave Jiang wrote:
>>
>> On 10/1/26 6:20 AM, alucerop@amd.com wrote:
>>> From: Alejandro Lucero <alucerop@amd.com>
>>>
>>> Use a new field in cxl_attach_region struct for easily link it with the
>>> region the memdev is attached to.
>>>
>>> This facilitates device links creation where such a region is the supplier
>>> with non-PF0 physical functions wanting to use the CXL region being the
>>> consumers.
>>>
>>> Signed-off-by: Alejandro Lucero <alucerop@amd.com>
>>> ---
>>>   drivers/cxl/core/region.c | 1 +
>>>   drivers/cxl/cxlmem.h      | 2 ++
>>>   2 files changed, 3 insertions(+)
>>>
>>> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
>>> index 27e63e6dab7c..78ca7ebc3e55 100644
>>> --- a/drivers/cxl/core/region.c
>>> +++ b/drivers/cxl/core/region.c
>>> @@ -4132,6 +4132,7 @@ int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
>>>       if (rc)
>>>           return rc;
>>>   +    attach->cxlr = cxlr;
>>>       attach->hpa_range = (struct range) {
>>>           .start = cxlr->params.res->start,
>>>           .end = cxlr->params.res->end,
>>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
>>> index c401e3a1af06..c598561b8e5f 100644
>>> --- a/drivers/cxl/cxlmem.h
>>> +++ b/drivers/cxl/cxlmem.h
>>> @@ -104,6 +104,7 @@ struct cxl_memdev_attach {
>>>   /**
>>>    * struct cxl_attach_region - coordinate mapping a region at memdev registration
>>>    * @attach: common core attachment descriptor
>>> + * @cxlr: cxl region the memdev is attached to.
>>>    * @hpa_range: physical address range of the region
>>>    *
>>>    * For the common simple case of a CXL device with private (non-general purpose
>>> @@ -112,6 +113,7 @@ struct cxl_memdev_attach {
>>>    */
>>>   struct cxl_attach_region {
>>>       struct cxl_memdev_attach attach;
>>> +    struct cxl_region *cxlr;
>>>       struct range hpa_range;
>>>   };
>>>   
>> attach->cxlr is never cleared when the region goes away. Unbinding the endpoint port runs endpoint_unregister_region(), which unregisters the region and drops its reference. PF0 stays bound until the detach work runs.
>>
>> A non-PF0 probe in that window still finds the memdev and calls device_link_add() on the freed region.
> 
> 
> I do not think so. This version, see next patch, relies on locking the supplier, PF0, before getting the memdev and potentially using the attach region. Only on PF0 release can such a memdev and region disappear, so I think this is enough.
> 

The memdev, yes. It's devm on PF0. The region, no. Unbinding the endpoint port, or cxl_mem from the memdev, runs endpoint_unregister_region() under the endpoint's lock, not PF0's. PF0's release is only queued (schedule_detach() -> detach_memdev()). Until that work runs, PF0 is bound, the memdev is found, hpa_range is valid, and attach->cxlr points at a freed region. Root teardown (kill_regions()) and delete_region also unregister the region without touching PF0.

I attached an LLM generated test kernel module you can use to reproduce the KASAN complaint. Commit log provides instructions.
BUG: KASAN: slab-use-after-free in device_link_add+0x521/0xa80
 cxl_get_range_and_link+0xa9/0x120 [cxl_core]

DJ

> I added comments in the exported function, cxl_get_range_and_link() which does the locking before calling the internal function __cxl_get_range_and_link() which looks for the memdev and the attach region. In fact, it should not be possible to obtain the PF0 memdev reference and the attach region not there yet, but the code is still checking that possibility as a sanity check.
> 
> 
> Thank you,
> 
> Alejandro.
> 
> 
>> How about something like this?
>>
>> diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
>> index 27e63e6dab7c..38ca73f12b84 100644
>> --- a/drivers/cxl/core/region.c
>> +++ b/drivers/cxl/core/region.c
>> @@ -4076,6 +4076,23 @@ static int first_mapped_decoder(struct device *dev, const void *data)
>>       return 0;
>>   }
>>   +/*
>> + * Invalidate @attach before the region goes away so that
>> + * cxl_get_range_and_link() can not pick up a stale region.
>> + */
>> +static void endpoint_detach_attach_region(void *_attach)
>> +{
>> +    struct cxl_attach_region *attach = _attach;
>> +    struct cxl_region *cxlr;
>> +
>> +    scoped_guard(rwsem_write, &cxl_rwsem.region) {
>> +        cxlr = attach->cxlr;
>> +        WRITE_ONCE(attach->cxlr, NULL);
>> +        attach->hpa_range = DEFINE_RANGE(0, -1);
>> +    }
>> +    endpoint_unregister_region(cxlr);
>> +}
>> +
>>   /*
>>    * Runs in cxl_mem_probe context after successful endpoint probe, assumes the
>>    * simple case of single mapped decoder per memdev.
>> @@ -4127,15 +4144,23 @@ int cxl_memdev_attach_region(struct cxl_memdev *cxlmd)
>>         /* Only teardown regions that pass validation, ignore the rest */
>>       get_device(&cxlr->dev);
>> -    rc = devm_add_action_or_reset(&endpoint->dev,
>> -                      endpoint_unregister_region, cxlr);
>> -    if (rc)
>> +    /*
>> +     * Not devm_add_action_or_reset(): the reset path would take
>> +     * cxl_rwsem.region for write while it is held for read here. The
>> +     * endpoint lock keeps the action from running before @attach is set.
>> +     */
>> +    rc = devm_add_action(&endpoint->dev, endpoint_detach_attach_region,
>> +                 attach);
>> +    if (rc) {
>> +        put_device(&cxlr->dev);
>>           return rc;
>> +    }
>>         attach->hpa_range = (struct range) {
>>           .start = cxlr->params.res->start,
>>           .end = cxlr->params.res->end,
>>       };
>> +    WRITE_ONCE(attach->cxlr, cxlr);
>>       return 0;
>>   }
>>   EXPORT_SYMBOL_FOR_MODULES(cxl_memdev_attach_region, "cxl_mem");
>> diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h
>> index c401e3a1af06..7cd3a69cd5f5 100644
>> --- a/drivers/cxl/cxlmem.h
>> +++ b/drivers/cxl/cxlmem.h
>> @@ -104,6 +104,8 @@ struct cxl_memdev_attach {
>>   /**
>>    * struct cxl_attach_region - coordinate mapping a region at memdev registration
>>    * @attach: common core attachment descriptor
>> + * @cxlr: cxl region the memdev is attached to, cleared under cxl_rwsem.region
>> + *    before the region is unregistered
>>    * @hpa_range: physical address range of the region
>>    *
>>    * For the common simple case of a CXL device with private (non-general purpose
>> @@ -112,6 +114,7 @@ struct cxl_memdev_attach {
>>    */
>>   struct cxl_attach_region {
>>       struct cxl_memdev_attach attach;
>> +    struct cxl_region *cxlr;
>>       struct range hpa_range;
>>   };
>>   
> 

[-- Attachment #2: 0001-TEST-ONLY-cxl-test-mock-non-PF0-consumer-for-cxl_get.patch --]
[-- Type: text/x-patch, Size: 4306 bytes --]

From d52f0776cbb6e1df45a6511010bce12f81cb2313 Mon Sep 17 00:00:00 2001
From: Dave Jiang <dave.jiang@intel.com>
Date: Thu, 1 Oct 2026 14:13:19 -0700
Subject: [PATCH] TEST ONLY: cxl/test: mock non-PF0 consumer for
 cxl_get_range_and_link()

Not for submission. Add cxl_mock_pfx, a platform driver whose probe calls
cxl_get_range_and_link() against the cxl_test type-2 accelerator
(cxl_type2_accel.0), the way a non-PF0 function would.

To race the device link against region teardown:

  modprobe cxl_test type2_test=1
  modprobe cxl_mock_pfx
  D=/sys/bus/platform/drivers/cxl_mock_pfx
  EP=$(ls /sys/bus/cxl/devices | grep endpoint)
  for i in $(seq 200); do
      echo cxl_mock_pfx.1 > $D/unbind 2>/dev/null
      echo cxl_mock_pfx.1 > $D/bind 2>/dev/null
  done &
  sleep 0.2
  echo $EP > /sys/bus/cxl/drivers/cxl_port/unbind
  wait

The endpoint name depends on the topology, so EP is looked up rather
than hard coded. type2_test=1 creates a single accelerator, so there is
one endpoint.

Repeat from the modprobe of cxl_test if the window is missed. With
KASAN, a stale attach->cxlr shows up as a slab-use-after-free in
device_link_add() called from cxl_get_range_and_link().

The use-after-free is only reported reliably with KASAN enabled. Without
it, the stale access may go unnoticed. Tested with:

  CONFIG_KASAN=y
  CONFIG_KASAN_GENERIC=y
---
 tools/testing/cxl/test/Kbuild |  2 +
 tools/testing/cxl/test/pfx.c  | 77 +++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)
 create mode 100644 tools/testing/cxl/test/pfx.c

diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
index 9a24ddc28488..279d89be4a4d 100644
--- a/tools/testing/cxl/test/Kbuild
+++ b/tools/testing/cxl/test/Kbuild
@@ -6,11 +6,13 @@ obj-m += cxl_mock.o
 obj-m += cxl_mock_mem.o
 obj-m += cxl_translate.o
 obj-m += cxl_mock_accel.o
+obj-m += cxl_mock_pfx.o
 
 cxl_test-y := cxl.o
 cxl_test-y += hmem_test.o
 cxl_mock-y := mock.o
 cxl_mock_mem-y := mem.o
 cxl_mock_accel-y := accel.o
+cxl_mock_pfx-y := pfx.o
 
 KBUILD_CFLAGS := $(filter-out -Wmissing-prototypes -Wmissing-declarations, $(KBUILD_CFLAGS))
diff --git a/tools/testing/cxl/test/pfx.c b/tools/testing/cxl/test/pfx.c
new file mode 100644
index 000000000000..39c0c081c896
--- /dev/null
+++ b/tools/testing/cxl/test/pfx.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TEST ONLY: mock non-PF0 consumer. Its probe links to the region attached
+ * to the cxl_test type-2 accelerator via cxl_get_range_and_link().
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <cxl/cxl.h>
+
+static char *pf0_name = "cxl_type2_accel.0";
+module_param(pf0_name, charp, 0444);
+
+static struct platform_device *pfx_pdev;
+
+static int cxl_mock_pfx_probe(struct platform_device *pdev)
+{
+	struct device *pf0;
+	struct range range;
+	int rc;
+
+	pf0 = bus_find_device_by_name(&platform_bus_type, NULL, pf0_name);
+	if (!pf0) {
+		dev_info(&pdev->dev, "pfx_test: %s not found\n", pf0_name);
+		return -ENODEV;
+	}
+
+	rc = cxl_get_range_and_link(pf0, &pdev->dev, &range);
+	put_device(pf0);
+	if (rc) {
+		dev_info(&pdev->dev, "pfx_test: link rc=%d\n", rc);
+		/* don't let -EPROBE_DEFER requeue us behind the test's back */
+		return rc == -EPROBE_DEFER ? -EAGAIN : rc;
+	}
+
+	dev_info(&pdev->dev, "pfx_test: link rc=0 range=%pra\n", &range);
+	return 0;
+}
+
+static void cxl_mock_pfx_remove(struct platform_device *pdev)
+{
+	dev_info(&pdev->dev, "pfx_test: removed\n");
+}
+
+static struct platform_driver cxl_mock_pfx_driver = {
+	.probe = cxl_mock_pfx_probe,
+	.remove = cxl_mock_pfx_remove,
+	.driver = {
+		.name = "cxl_mock_pfx",
+	},
+};
+
+static int __init cxl_mock_pfx_init(void)
+{
+	int rc;
+
+	pfx_pdev = platform_device_register_simple("cxl_mock_pfx", 1, NULL, 0);
+	if (IS_ERR(pfx_pdev))
+		return PTR_ERR(pfx_pdev);
+
+	rc = platform_driver_register(&cxl_mock_pfx_driver);
+	if (rc)
+		platform_device_unregister(pfx_pdev);
+	return rc;
+}
+module_init(cxl_mock_pfx_init);
+
+static void __exit cxl_mock_pfx_exit(void)
+{
+	platform_driver_unregister(&cxl_mock_pfx_driver);
+	platform_device_unregister(pfx_pdev);
+}
+module_exit(cxl_mock_pfx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("cxl_test: TEST ONLY mock non-PF0 consumer");
+MODULE_IMPORT_NS("CXL");
-- 
2.54.0


  reply	other threads:[~2026-10-02 15:52 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-01 13:20 [PATCH v2 0/4] Type2 multipf support alucerop
2026-10-01 13:20 ` [PATCH v2 1/4] driver core: Check for supplier requiring PM at link creation alucerop
2026-10-01 20:31   ` Dave Jiang
2026-10-02  4:32     ` Lucero Palau, Alejandro
2026-10-02 15:31       ` Dave Jiang
2026-10-01 13:20 ` [PATCH v2 2/4] cxl/region: Add region reference in memdev attach alucerop
2026-10-01 21:38   ` Dave Jiang
2026-10-02  4:41     ` Lucero Palau, Alejandro
2026-10-02 15:52       ` Dave Jiang [this message]
2026-10-08 13:50         ` Lucero Palau, Alejandro
2026-10-08 16:18           ` Dave Jiang
2026-10-08 18:07             ` Lucero Palau, Alejandro
2026-10-08 21:05               ` Dave Jiang
2026-10-09  6:58                 ` Lucero Palau, Alejandro
2026-10-01 13:20 ` [PATCH v2 3/4] cxl/memdev: Add support for multi PF devices alucerop
2026-10-01 22:11   ` Dave Jiang
2026-10-01 22:41     ` Dave Jiang
2026-10-02  4:50     ` Lucero Palau, Alejandro
2026-10-02 15:55       ` Dave Jiang
2026-10-01 13:20 ` [PATCH v2 4/4] sfc: add multipf support alucerop
2026-10-01 22:32   ` Dave Jiang
2026-10-02  5:33     ` Lucero Palau, Alejandro

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bd412198-8a25-436c-b6d6-6b6730e8c28e@intel.com \
    --to=dave.jiang@intel.com \
    --cc=alejandro.lucero-palau@amd.com \
    --cc=alucerop@amd.com \
    --cc=davem@davemloft.net \
    --cc=ecree.xilinx@gmail.com \
    --cc=edumazet@google.com \
    --cc=icheng@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=rafael@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox