Linux CXL
 help / color / mirror / Atom feed
* [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
@ 2026-09-10  9:38 Junjie Cao
  2026-09-10  9:54 ` sashiko-bot
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Junjie Cao @ 2026-09-10  9:38 UTC (permalink / raw)
  To: Dave Jiang, Jonathan Cameron, Jonathan Cameron, Davidlohr Bueso,
	Alison Schofield, Vishal Verma, Dan Williams, linux-cxl
  Cc: Ira Weiny, Li Ming, Richard Cheng, linux-kernel

cxl_decode_regblock() only checks that the Register Locator offset fits
pci_resource_len().  A BAR the PCI core could not place is reset to zero
start, end and flags while config space keeps the firmware value, so a
zero offset passes and the block is mapped at physical address 0.  On
x86 ioremap() of the reserved low megabyte succeeds, the component
register header does not match, and a switch port fails with "HDM
decoder capability not found", pointing at the HDM decoders rather than
at the BAR.

Reject a BAR with no length or still unassigned, next to the existing size
check.  Skip empty Register Locator entries first: they decode as BAR0
offset 0 and would trip the check, and nothing looks them up.

Seen with edk2-stable202602 (f6489621b8ae, reverted in stable202605),
which places 64-bit non-prefetchable BARs behind a bridge in the
prefetchable window.  Linux refuses to claim them and the switch
upstream and downstream port BAR0s find no room in the 32-bit window.
QEMU q35 with a CXL switch, Fedora 43 edk2-ovmf-20260213, before:

pci 0000:0d:00.0: BAR 0 [mem size 0x00010000 64bit]: can't assign; no space
cxl_port port2: HDM decoder capability not found

after:

pcieport 0000:0d:00.0: BAR0: not assigned (type: 1)
cxl_port port2: No component registers mapped
cxl_port port2: Failed to map HDM decoder capability

Link: https://github.com/tianocore/edk2/issues/13104
Signed-off-by: Junjie Cao <junjie.cao@intel.com>
---
 drivers/cxl/core/regs.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
index 20c2d9fbcfe7..41416fa2ce4a 100644
--- a/drivers/cxl/core/regs.c
+++ b/drivers/cxl/core/regs.c
@@ -277,6 +277,20 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
 	u64 offset = ((u64)reg_hi << 32) |
 		     (reg_lo & PCI_DVSEC_CXL_REG_LOCATOR_BLOCK_OFF_LOW);
 
+	if (reg_type == CXL_REGLOC_RBI_EMPTY)
+		return false;
+
+	/*
+	 * A BAR the PCI core could not place is reset to zero; decoding it
+	 * would map the block at physical address 0.
+	 */
+	if (!pci_resource_len(pdev, bar) ||
+	    (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)) {
+		dev_warn(&pdev->dev, "BAR%d: not assigned (type: %d)\n", bar,
+			 reg_type);
+		return false;
+	}
+
 	if (offset > pci_resource_len(pdev, bar)) {
 		dev_warn(&pdev->dev,
 			 "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
  2026-09-10  9:38 [PATCH] cxl/regs: Reject register blocks in an unassigned BAR Junjie Cao
@ 2026-09-10  9:54 ` sashiko-bot
  2026-09-10 15:38 ` Dave Jiang
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-09-10  9:54 UTC (permalink / raw)
  To: Junjie Cao; +Cc: linux-cxl

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing validation of CXL register locator BIR and resource type allows mapping non-memory or bridge window resources, leading to memory corruption.
- [High] Insufficient bounds validation allows out-of-bounds MMIO reads during CXL capabilities probing, leading to kernel panics.
--

commit 1001d63b6bc0f0959235c2291ef3e197cc4ca650
Author: Junjie Cao <junjie.cao@intel.com>

cxl/regs: Reject register blocks in an unassigned BAR

cxl_decode_regblock() only checks that the Register Locator offset fits
pci_resource_len(). A BAR the PCI core could not place is reset to zero
start, end and flags while config space keeps the firmware value, so a
zero offset passes and the block is mapped at physical address 0.

Link: https://github.com/tianocore/edk2/issues/13104

> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 20c2d9fbcfe7d..41416fa2ce4a4 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -277,6 +277,20 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
>  	u64 offset = ((u64)reg_hi << 32) |
>  		     (reg_lo & PCI_DVSEC_CXL_REG_LOCATOR_BLOCK_OFF_LOW);
>  
> +	if (reg_type == CXL_REGLOC_RBI_EMPTY)
> +		return false;
> +
> +	/*
> +	 * A BAR the PCI core could not place is reset to zero; decoding it
> +	 * would map the block at physical address 0.
> +	 */
> +	if (!pci_resource_len(pdev, bar) ||
> +	    (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)) {

[Severity: High]
This isn't a bug introduced by this patch, but since bar is extracted from the
untrusted CXL DVSEC register locator before this check, is there anything
verifying that it falls within the standard endpoint BAR range (0-5)?

Additionally, could a malicious device bypass this validation by configuring a
standard BAR as an assigned I/O BAR (IORESOURCE_IO)? This code does not
verify if pci_resource_flags(pdev, bar) & IORESOURCE_MEM is true. 

If an I/O port address bypasses this check and is passed to ioremap(), could
it map low physical RAM on x86 and corrupt memory during register setup?

> +		dev_warn(&pdev->dev, "BAR%d: not assigned (type: %d)\n", bar,
> +			 reg_type);
> +		return false;
> +	}
> +
>  	if (offset > pci_resource_len(pdev, bar)) {

[Severity: High]
This is a pre-existing issue, but this bounds check allows offset to be
arbitrarily close or equal to the resource length without ensuring enough
space remains for the capabilities array.

If this computes a very small max_size (such as 4 bytes), wouldn't the driver
subsequently perform out-of-bounds MMIO reads during capabilities probing:

drivers/cxl/core/regs.c:cxl_probe_device_regs() {
    ...
    for (cap = 1; cap <= cap_count; cap++) {
        cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
                           readl(base + cap * 0x10));
    ...
}

and read past the end of the mapped pages, causing a kernel panic?

>  		dev_warn(&pdev->dev,
>  			 "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,
>  			 &pdev->resource[bar], &offset, reg_type);
>  		return false;
>  	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910093858.535969-1-junjie.cao@intel.com?part=1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
  2026-09-10  9:38 [PATCH] cxl/regs: Reject register blocks in an unassigned BAR Junjie Cao
  2026-09-10  9:54 ` sashiko-bot
@ 2026-09-10 15:38 ` Dave Jiang
  2026-09-10 20:55   ` Jonathan Cameron
  2026-09-11  9:50 ` Li Ming
  2026-09-11 16:04 ` Dave Jiang
  3 siblings, 1 reply; 6+ messages in thread
From: Dave Jiang @ 2026-09-10 15:38 UTC (permalink / raw)
  To: Junjie Cao, Jonathan Cameron, Jonathan Cameron, Davidlohr Bueso,
	Alison Schofield, Vishal Verma, Dan Williams, linux-cxl
  Cc: Ira Weiny, Li Ming, Richard Cheng, linux-kernel



On 9/10/26 2:38 AM, Junjie Cao wrote:
> cxl_decode_regblock() only checks that the Register Locator offset fits
> pci_resource_len().  A BAR the PCI core could not place is reset to zero
> start, end and flags while config space keeps the firmware value, so a
> zero offset passes and the block is mapped at physical address 0.  On
> x86 ioremap() of the reserved low megabyte succeeds, the component
> register header does not match, and a switch port fails with "HDM
> decoder capability not found", pointing at the HDM decoders rather than
> at the BAR.
> 
> Reject a BAR with no length or still unassigned, next to the existing size
> check.  Skip empty Register Locator entries first: they decode as BAR0
> offset 0 and would trip the check, and nothing looks them up.
> 
> Seen with edk2-stable202602 (f6489621b8ae, reverted in stable202605),
> which places 64-bit non-prefetchable BARs behind a bridge in the
> prefetchable window.  Linux refuses to claim them and the switch
> upstream and downstream port BAR0s find no room in the 32-bit window.
> QEMU q35 with a CXL switch, Fedora 43 edk2-ovmf-20260213, before:
> 
> pci 0000:0d:00.0: BAR 0 [mem size 0x00010000 64bit]: can't assign; no space
> cxl_port port2: HDM decoder capability not found
> 
> after:
> 
> pcieport 0000:0d:00.0: BAR0: not assigned (type: 1)
> cxl_port port2: No component registers mapped
> cxl_port port2: Failed to map HDM decoder capability
> 
> Link: https://github.com/tianocore/edk2/issues/13104
> Signed-off-by: Junjie Cao <junjie.cao@intel.com>

Reviewed-by: Dave Jiang <dave.jiang@intel.com>

> ---
>  drivers/cxl/core/regs.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 20c2d9fbcfe7..41416fa2ce4a 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -277,6 +277,20 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
>  	u64 offset = ((u64)reg_hi << 32) |
>  		     (reg_lo & PCI_DVSEC_CXL_REG_LOCATOR_BLOCK_OFF_LOW);
>  
> +	if (reg_type == CXL_REGLOC_RBI_EMPTY)
> +		return false;
> +
> +	/*
> +	 * A BAR the PCI core could not place is reset to zero; decoding it
> +	 * would map the block at physical address 0.
> +	 */
> +	if (!pci_resource_len(pdev, bar) ||
> +	    (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)) {
> +		dev_warn(&pdev->dev, "BAR%d: not assigned (type: %d)\n", bar,
> +			 reg_type);
> +		return false;
> +	}
> +
>  	if (offset > pci_resource_len(pdev, bar)) {
>  		dev_warn(&pdev->dev,
>  			 "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
  2026-09-10 15:38 ` Dave Jiang
@ 2026-09-10 20:55   ` Jonathan Cameron
  0 siblings, 0 replies; 6+ messages in thread
From: Jonathan Cameron @ 2026-09-10 20:55 UTC (permalink / raw)
  To: Dave Jiang
  Cc: Junjie Cao, Jonathan Cameron, Davidlohr Bueso, Alison Schofield,
	Vishal Verma, Dan Williams, linux-cxl, Ira Weiny, Li Ming,
	Richard Cheng, linux-kernel

On Thu, 10 Sep 2026 08:38:06 -0700
Dave Jiang <dave.jiang@intel.com> wrote:

> On 9/10/26 2:38 AM, Junjie Cao wrote:
> > cxl_decode_regblock() only checks that the Register Locator offset fits
> > pci_resource_len().  A BAR the PCI core could not place is reset to zero
> > start, end and flags while config space keeps the firmware value, so a
> > zero offset passes and the block is mapped at physical address 0.  On
> > x86 ioremap() of the reserved low megabyte succeeds, the component
> > register header does not match, and a switch port fails with "HDM
> > decoder capability not found", pointing at the HDM decoders rather than
> > at the BAR.
> > 
> > Reject a BAR with no length or still unassigned, next to the existing size
> > check.  Skip empty Register Locator entries first: they decode as BAR0
> > offset 0 and would trip the check, and nothing looks them up.
> > 
> > Seen with edk2-stable202602 (f6489621b8ae, reverted in stable202605),
> > which places 64-bit non-prefetchable BARs behind a bridge in the
> > prefetchable window.  Linux refuses to claim them and the switch
> > upstream and downstream port BAR0s find no room in the 32-bit window.
> > QEMU q35 with a CXL switch, Fedora 43 edk2-ovmf-20260213, before:
> > 
> > pci 0000:0d:00.0: BAR 0 [mem size 0x00010000 64bit]: can't assign; no space
> > cxl_port port2: HDM decoder capability not found
> > 
> > after:
> > 
> > pcieport 0000:0d:00.0: BAR0: not assigned (type: 1)
> > cxl_port port2: No component registers mapped
> > cxl_port port2: Failed to map HDM decoder capability
> > 
> > Link: https://github.com/tianocore/edk2/issues/13104
> > Signed-off-by: Junjie Cao <junjie.cao@intel.com>  
> 
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>

Reviewed-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
  2026-09-10  9:38 [PATCH] cxl/regs: Reject register blocks in an unassigned BAR Junjie Cao
  2026-09-10  9:54 ` sashiko-bot
  2026-09-10 15:38 ` Dave Jiang
@ 2026-09-11  9:50 ` Li Ming
  2026-09-11 16:04 ` Dave Jiang
  3 siblings, 0 replies; 6+ messages in thread
From: Li Ming @ 2026-09-11  9:50 UTC (permalink / raw)
  To: Junjie Cao, Dave Jiang, Jonathan Cameron, Jonathan Cameron,
	Davidlohr Bueso, Alison Schofield, Vishal Verma, Dan Williams,
	linux-cxl
  Cc: Ira Weiny, Richard Cheng, linux-kernel

On 9/10/2026 5:38 PM, Junjie Cao wrote:
> cxl_decode_regblock() only checks that the Register Locator offset fits
> pci_resource_len().  A BAR the PCI core could not place is reset to zero
> start, end and flags while config space keeps the firmware value, so a
> zero offset passes and the block is mapped at physical address 0.  On
> x86 ioremap() of the reserved low megabyte succeeds, the component
> register header does not match, and a switch port fails with "HDM
> decoder capability not found", pointing at the HDM decoders rather than
> at the BAR.
>
> Reject a BAR with no length or still unassigned, next to the existing size
> check.  Skip empty Register Locator entries first: they decode as BAR0
> offset 0 and would trip the check, and nothing looks them up.
>
> Seen with edk2-stable202602 (f6489621b8ae, reverted in stable202605),
> which places 64-bit non-prefetchable BARs behind a bridge in the
> prefetchable window.  Linux refuses to claim them and the switch
> upstream and downstream port BAR0s find no room in the 32-bit window.
> QEMU q35 with a CXL switch, Fedora 43 edk2-ovmf-20260213, before:
>
> pci 0000:0d:00.0: BAR 0 [mem size 0x00010000 64bit]: can't assign; no space
> cxl_port port2: HDM decoder capability not found
>
> after:
>
> pcieport 0000:0d:00.0: BAR0: not assigned (type: 1)
> cxl_port port2: No component registers mapped
> cxl_port port2: Failed to map HDM decoder capability
>
> Link: https://github.com/tianocore/edk2/issues/13104
> Signed-off-by: Junjie Cao <junjie.cao@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
> ---
>  drivers/cxl/core/regs.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 20c2d9fbcfe7..41416fa2ce4a 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -277,6 +277,20 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
>  	u64 offset = ((u64)reg_hi << 32) |
>  		     (reg_lo & PCI_DVSEC_CXL_REG_LOCATOR_BLOCK_OFF_LOW);
>  
> +	if (reg_type == CXL_REGLOC_RBI_EMPTY)
> +		return false;
> +
> +	/*
> +	 * A BAR the PCI core could not place is reset to zero; decoding it
> +	 * would map the block at physical address 0.
> +	 */
> +	if (!pci_resource_len(pdev, bar) ||
> +	    (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)) {
> +		dev_warn(&pdev->dev, "BAR%d: not assigned (type: %d)\n", bar,
> +			 reg_type);
> +		return false;
> +	}
> +
>  	if (offset > pci_resource_len(pdev, bar)) {
>  		dev_warn(&pdev->dev,
>  			 "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] cxl/regs: Reject register blocks in an unassigned BAR
  2026-09-10  9:38 [PATCH] cxl/regs: Reject register blocks in an unassigned BAR Junjie Cao
                   ` (2 preceding siblings ...)
  2026-09-11  9:50 ` Li Ming
@ 2026-09-11 16:04 ` Dave Jiang
  3 siblings, 0 replies; 6+ messages in thread
From: Dave Jiang @ 2026-09-11 16:04 UTC (permalink / raw)
  To: Junjie Cao, Jonathan Cameron, Jonathan Cameron, Davidlohr Bueso,
	Alison Schofield, Vishal Verma, Dan Williams, linux-cxl
  Cc: Ira Weiny, Li Ming, Richard Cheng, linux-kernel



On 9/10/26 2:38 AM, Junjie Cao wrote:
> cxl_decode_regblock() only checks that the Register Locator offset fits
> pci_resource_len().  A BAR the PCI core could not place is reset to zero
> start, end and flags while config space keeps the firmware value, so a
> zero offset passes and the block is mapped at physical address 0.  On
> x86 ioremap() of the reserved low megabyte succeeds, the component
> register header does not match, and a switch port fails with "HDM
> decoder capability not found", pointing at the HDM decoders rather than
> at the BAR.
> 
> Reject a BAR with no length or still unassigned, next to the existing size
> check.  Skip empty Register Locator entries first: they decode as BAR0
> offset 0 and would trip the check, and nothing looks them up.
> 
> Seen with edk2-stable202602 (f6489621b8ae, reverted in stable202605),
> which places 64-bit non-prefetchable BARs behind a bridge in the
> prefetchable window.  Linux refuses to claim them and the switch
> upstream and downstream port BAR0s find no room in the 32-bit window.
> QEMU q35 with a CXL switch, Fedora 43 edk2-ovmf-20260213, before:
> 
> pci 0000:0d:00.0: BAR 0 [mem size 0x00010000 64bit]: can't assign; no space
> cxl_port port2: HDM decoder capability not found
> 
> after:
> 
> pcieport 0000:0d:00.0: BAR0: not assigned (type: 1)
> cxl_port port2: No component registers mapped
> cxl_port port2: Failed to map HDM decoder capability
> 
> Link: https://github.com/tianocore/edk2/issues/13104
> Signed-off-by: Junjie Cao <junjie.cao@intel.com>

Applied to cxl/next:
dc96f35358a1

> ---
>  drivers/cxl/core/regs.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/cxl/core/regs.c b/drivers/cxl/core/regs.c
> index 20c2d9fbcfe7..41416fa2ce4a 100644
> --- a/drivers/cxl/core/regs.c
> +++ b/drivers/cxl/core/regs.c
> @@ -277,6 +277,20 @@ static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
>  	u64 offset = ((u64)reg_hi << 32) |
>  		     (reg_lo & PCI_DVSEC_CXL_REG_LOCATOR_BLOCK_OFF_LOW);
>  
> +	if (reg_type == CXL_REGLOC_RBI_EMPTY)
> +		return false;
> +
> +	/*
> +	 * A BAR the PCI core could not place is reset to zero; decoding it
> +	 * would map the block at physical address 0.
> +	 */
> +	if (!pci_resource_len(pdev, bar) ||
> +	    (pci_resource_flags(pdev, bar) & IORESOURCE_UNSET)) {
> +		dev_warn(&pdev->dev, "BAR%d: not assigned (type: %d)\n", bar,
> +			 reg_type);
> +		return false;
> +	}
> +
>  	if (offset > pci_resource_len(pdev, bar)) {
>  		dev_warn(&pdev->dev,
>  			 "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-11 16:04 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10  9:38 [PATCH] cxl/regs: Reject register blocks in an unassigned BAR Junjie Cao
2026-09-10  9:54 ` sashiko-bot
2026-09-10 15:38 ` Dave Jiang
2026-09-10 20:55   ` Jonathan Cameron
2026-09-11  9:50 ` Li Ming
2026-09-11 16:04 ` Dave Jiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox