DMA access control / memory safety bypass via P2PDMA to non-mappable PCI BARs
Description
This commit hardens access controls for PCIe P2PDMA by blocking DMA provider creation and CPU access for non_mappable BARs. Specifically:
- In pcim_p2pdma_init, if the PCI device has non_mappable_bars set, the function now returns -EOPNOTSUPP, preventing P2PDMA initialization for that device.
- In pcim_p2pdma_provider, if non_mappable_bars is set, no provider is created for the given BAR.
- The device whitelist is updated to include Intel DSA, IAA, and QAT devices, presumably to permit P2PDMA usage for these devices while still enforcing non_mappable_bars restrictions.
- The non_mappable_bars documentation is clarified to indicate that CPU or peer access is restricted, reinforcing that such BARs should not be materialized for DMA by either CPU or P2PDMA.
Overall, this is a real vulnerability fix addressing improper DMA access to non-mappable PCI BARs, tightening memory safety and access control for DMA pathways.
Proof of Concept
PoC outline (conceptual, targeted at a pre-fix kernel, i.e., <= v7.0-rc6):
Prereqs:
- A Linux kernel prior to this commit’s protection (<= v7.0-rc6) with P2PDMA support and a PCI device that has non_mappable_bars set (e.g., Intel QAT/DSA/IAA devices).
- A privileged user (CAP_SYS_ADMIN) or kernel module capable of interacting with P2PDMA internals or triggering its initialization.
- A test setup that allows DMA operations via P2PDMA against a memory region the CPU should not be able to map.
Steps (exploit path before patch):
1) Identify a target device with non_mappable_bars set and that is included in the P2PDMA whitelist (e.g., Intel QAT/DSA/IAA prior to patch).
- Example: lspci -nn -d <vendor:device> to locate the device, then inspect the driver's non_mappable_bars handling.
2) Trigger P2PDMA initialization for that device and BAR.
- Before the fix, pcim_p2pdma_init would proceed and create p2pdma structures even when non_mappable_bars is set; after the patch, the init would return -EOPNOTSUPP for non_mappable_bars devices.
3) If a provider for a non-mappable BAR is created (pre-fix behavior), request a P2PDMA DMA channel targeting an attacker-controlled or kernel memory region that the CPU cannot map (e.g., a non-mappable BAR backing store or a kernel-memory-like region).
4) Perform a DMA transfer (PCIe P2PDMA) to copy data from the attacker-controlled buffer into the restricted memory region or vice versa.
5) Verify memory effects:
- Data leaks from kernel or restricted regions into the BAR region, or memory corruption if the BAR maps to executable/critical regions.
- Potential memory disclosure or tampering via DMA to non-mapped memory.
6) After applying the patch, re-run the same steps and observe that P2PDMA initialization fails for non_mappable_bars devices, and provider creation is blocked, preventing the DMA path.
Notes:
- The exact P2PDMA user-space or kernel API surface is internal; this PoC outlines the attack flow and relies on the ability to initialize P2PDMA for a non-mappable BAR and then perform a DMA operation to an unmapped memory region. The patch blocks initialization and provider creation for such BARs, which should stop the exploit path.
Commit Details
Author: Bjorn Helgaas
Date: 2026-06-23 22:32 UTC
Message:
Merge branch 'pci/p2pdma'
- Prevent P2PDMA as well as CPU access to non-mappable BARs, e.g., s390 ISM
BARs (Matt Evans)
- Add Intel QAT, DSA, IAA devices to whitelist (Lukas Wunner)
* pci/p2pdma:
PCI/P2PDMA: Add Intel QAT, DSA, IAA devices to whitelist
PCI/P2PDMA: Avoid returning a provider for non_mappable_bars
Triage Assessment
Vulnerability Type: Memory safety / DMA access
Confidence: HIGH
Reasoning:
The patch prevents access to non-mappable PCI BARs by both P2PDMA and the CPU, and guards provider creation when non-mappable_bars is set. This tightens DMA access controls and mitigates potential DMA-based or memory safety vulnerabilities by restricting untrusted access to certain BARs. It also updates the device whitelist to explicitly include certain Intel devices that may rely on P2PDMA while still enforcing the non-mappable_bar restriction for others.
Verification Assessment
Vulnerability Type: DMA access control / memory safety bypass via P2PDMA to non-mappable PCI BARs
Confidence: HIGH
Affected Versions: <= v7.0-rc6
Code Diff
diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
index 03a4e969020805..cbd1d1eda5a345 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
+++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
@@ -28,15 +28,10 @@
#define ADF_4XXX_DEVICE_NAME "4xxx"
#define ADF_420XX_DEVICE_NAME "420xx"
#define ADF_6XXX_DEVICE_NAME "6xxx"
-#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940
#define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941
-#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942
#define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943
-#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944
#define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945
-#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946
#define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947
-#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948
#define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949
#define ADF_DEVICE_FUSECTL_OFFSET 0x40
diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h
index f95411363ea9aa..1dce26d4da8350 100644
--- a/drivers/dma/idxd/registers.h
+++ b/drivers/dma/idxd/registers.h
@@ -10,9 +10,6 @@
#endif
/* PCI Config */
-#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb
-#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212
-#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216
#define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d
#define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 7c898542af8d5e..b2d5266f86530e 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -262,6 +262,9 @@ int pcim_p2pdma_init(struct pci_dev *pdev)
struct pci_p2pdma *p2p;
int i, ret;
+ if (pdev->non_mappable_bars)
+ return -EOPNOTSUPP;
+
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
if (p2p)
return 0;
@@ -318,7 +321,8 @@ struct p2pdma_provider *pcim_p2pdma_provider(struct pci_dev *pdev, int bar)
{
struct pci_p2pdma *p2p;
- if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
+ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM) ||
+ pdev->non_mappable_bars)
return NULL;
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
@@ -548,6 +552,16 @@ static const struct pci_p2pdma_whitelist_entry {
{PCI_VENDOR_ID_INTEL, 0x2033, 0},
{PCI_VENDOR_ID_INTEL, 0x2020, 0},
{PCI_VENDOR_ID_INTEL, 0x09a2, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_GNRD, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_DMR, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAA_DMR, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX, 0},
+ {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0},
/* Google SoCs. */
{PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0},
{}
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 2c4454583c115b..1e6802017d6b8d 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -508,7 +508,7 @@ struct pci_dev {
unsigned int no_command_memory:1; /* No PCI_COMMAND_MEMORY */
unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */
unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */
- unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */
+ unsigned int non_mappable_bars:1; /* BARs can't be mapped by CPU or peers */
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 24cb42f66e4b60..1c9d40e09107dc 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2732,6 +2732,9 @@
#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130
#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132
#define PCI_DEVICE_ID_INTEL_SST_TNG 0x119a
+#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb
+#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212
+#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216
#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221
#define PCI_DEVICE_ID_INTEL_82437 0x122d
#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e
@@ -3052,6 +3055,11 @@
#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036
#define PCI_DEVICE_ID_INTEL_HDA_TGL_H 0x43c8
#define PCI_DEVICE_ID_INTEL_HDA_DG1 0x490d
+#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940
+#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942
+#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944
+#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946
+#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948
#define PCI_DEVICE_ID_INTEL_HDA_EHL_0 0x4b55
#define PCI_DEVICE_ID_INTEL_HDA_EHL_3 0x4b58
#define PCI_DEVICE_ID_INTEL_HDA_WCL 0x4d28