Skip to content

Commit 1809a00

Browse files
John W. Linvillelinvjw
authored andcommitted
cxl/mem: Fix no cxl_nvd during pmem region auto-assembling
JIRA: https://issues.redhat.com/browse/RHEL-51364 CVE: CVE-2024-41085 When CXL subsystem is auto-assembling a pmem region during cxl endpoint port probing, always hit below calltrace. BUG: kernel NULL pointer dereference, address: 0000000000000078 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page RIP: 0010:cxl_pmem_region_probe+0x22e/0x360 [cxl_pmem] Call Trace: <TASK> ? __die+0x24/0x70 ? page_fault_oops+0x82/0x160 ? do_user_addr_fault+0x65/0x6b0 ? exc_page_fault+0x7d/0x170 ? asm_exc_page_fault+0x26/0x30 ? cxl_pmem_region_probe+0x22e/0x360 [cxl_pmem] ? cxl_pmem_region_probe+0x1ac/0x360 [cxl_pmem] cxl_bus_probe+0x1b/0x60 [cxl_core] really_probe+0x173/0x410 ? __pfx___device_attach_driver+0x10/0x10 __driver_probe_device+0x80/0x170 driver_probe_device+0x1e/0x90 __device_attach_driver+0x90/0x120 bus_for_each_drv+0x84/0xe0 __device_attach+0xbc/0x1f0 bus_probe_device+0x90/0xa0 device_add+0x51c/0x710 devm_cxl_add_pmem_region+0x1b5/0x380 [cxl_core] cxl_bus_probe+0x1b/0x60 [cxl_core] The cxl_nvd of the memdev needs to be available during the pmem region probe. Currently the cxl_nvd is registered after the endpoint port probe. The endpoint probe, in the case of autoassembly of regions, can cause a pmem region probe requiring the not yet available cxl_nvd. Adjust the sequence so this dependency is met. This requires adding a port parameter to cxl_find_nvdimm_bridge() that can be used to query the ancestor root port. The endpoint port is not yet available, but will share a common ancestor with its parent, so start the query from there instead. Fixes: f17b558 ("cxl/pmem: Refactor nvdimm device registration, delete the workqueue") Co-developed-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Li Ming <ming4.li@intel.com> Tested-by: Alison Schofield <alison.schofield@intel.com> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Reviewed-by: Alison Schofield <alison.schofield@intel.com> Link: https://patch.msgid.link/20240612064423.2567625-1-ming4.li@intel.com Signed-off-by: Dave Jiang <dave.jiang@intel.com> (cherry picked from commit 84ec985) Conflicts: (context fixups, revisions to build correctly) drivers/cxl/core/pmem.c drivers/cxl/core/region.c Signed-off-by: John W. Linville <linville@redhat.com>
1 parent ffa09cd commit 1809a00

File tree

4 files changed

+26
-18
lines changed

4 files changed

+26
-18
lines changed

drivers/cxl/core/pmem.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,21 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
6262
return is_cxl_nvdimm_bridge(dev);
6363
}
6464

65-
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd)
65+
/**
66+
* cxl_find_nvdimm_bridge() - find a bridge device relative to a port
67+
* @port: any descendant port of an nvdimm-bridge associated
68+
* root-cxl-port
69+
*/
70+
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port)
6671
{
67-
struct cxl_port *port = find_cxl_root(cxlmd->endpoint);
72+
struct cxl_port *root_port = find_cxl_root(port);
6873
struct device *dev;
6974

70-
if (!port)
75+
if (!root_port)
7176
return NULL;
7277

73-
dev = device_find_child(&port->dev, NULL, match_nvdimm_bridge);
74-
put_device(&port->dev);
78+
dev = device_find_child(&root_port->dev, NULL, match_nvdimm_bridge);
79+
put_device(&root_port->dev);
7580

7681
if (!dev)
7782
return NULL;
@@ -242,18 +247,20 @@ static void cxlmd_release_nvdimm(void *_cxlmd)
242247

243248
/**
244249
* devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
250+
* @parent_port: parent port for the (to be added) @cxlmd endpoint port
245251
* @cxlmd: cxl_memdev instance that will perform LIBNVDIMM operations
246252
*
247253
* Return: 0 on success negative error code on failure.
248254
*/
249-
int devm_cxl_add_nvdimm(struct cxl_memdev *cxlmd)
255+
int devm_cxl_add_nvdimm(struct cxl_port *parent_port,
256+
struct cxl_memdev *cxlmd)
250257
{
251258
struct cxl_nvdimm_bridge *cxl_nvb;
252259
struct cxl_nvdimm *cxl_nvd;
253260
struct device *dev;
254261
int rc;
255262

256-
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd);
263+
cxl_nvb = cxl_find_nvdimm_bridge(parent_port);
257264
if (!cxl_nvb)
258265
return -ENODEV;
259266

drivers/cxl/core/region.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2517,7 +2517,7 @@ static struct cxl_pmem_region *cxl_pmem_region_alloc(struct cxl_region *cxlr)
25172517
* bridge for one device is the same for all.
25182518
*/
25192519
if (i == 0) {
2520-
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd);
2520+
cxl_nvb = cxl_find_nvdimm_bridge(cxlmd->endpoint);
25212521
if (!cxl_nvb) {
25222522
kfree(cxlr_pmem);
25232523
cxlr_pmem = ERR_PTR(-ENODEV);

drivers/cxl/cxl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -784,8 +784,8 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
784784
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
785785
bool is_cxl_nvdimm(struct device *dev);
786786
bool is_cxl_nvdimm_bridge(struct device *dev);
787-
int devm_cxl_add_nvdimm(struct cxl_memdev *cxlmd);
788-
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd);
787+
int devm_cxl_add_nvdimm(struct cxl_port *parent_port, struct cxl_memdev *cxlmd);
788+
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port);
789789

790790
#ifdef CONFIG_CXL_REGION
791791
bool is_cxl_pmem_region(struct device *dev);

drivers/cxl/mem.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,15 @@ static int cxl_mem_probe(struct device *dev)
153153
return -ENXIO;
154154
}
155155

156+
if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) {
157+
rc = devm_cxl_add_nvdimm(parent_port, cxlmd);
158+
if (rc) {
159+
if (rc == -ENODEV)
160+
dev_info(dev, "PMEM disabled by platform\n");
161+
return rc;
162+
}
163+
}
164+
156165
if (dport->rch)
157166
endpoint_parent = parent_port->uport_dev;
158167
else
@@ -173,14 +182,6 @@ static int cxl_mem_probe(struct device *dev)
173182
if (rc)
174183
return rc;
175184

176-
if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM)) {
177-
rc = devm_cxl_add_nvdimm(cxlmd);
178-
if (rc == -ENODEV)
179-
dev_info(dev, "PMEM disabled by platform\n");
180-
else
181-
return rc;
182-
}
183-
184185
/*
185186
* The kernel may be operating out of CXL memory on this device,
186187
* there is no spec defined way to determine whether this device

0 commit comments

Comments
 (0)