CVE-2026-45880
Description
In the Linux kernel, the following vulnerability has been resolved:
PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
When vm_insert_page() fails in p2pmem_alloc_mmap(), p2pmem_alloc_mmap() doesn't invoke percpu_ref_put() to free the per-CPU ref of pgmap acquired after gen_pool_alloc_owner(), and memunmap_pages() will hang forever when trying to remove the PCI device.
Fix it by adding the missed percpu_ref_put().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In the Linux kernel's PCI/P2PDMA, missing percpu_ref_put when vm_insert_page() fails causes device removal hang.
Vulnerability
In the Linux kernel's PCI/P2PDMA subsystem, the function p2pmem_alloc_mmap() fails to call percpu_ref_put() to release the per-CPU reference count of the pgmap when vm_insert_page() fails. This missing reference release occurs after a successful gen_pool_alloc_owner() call [1]. The issue affects kernel versions prior to the commit that adds the missing percpu_ref_put().
Exploitation
To trigger the vulnerability, the attacker must cause vm_insert_page() to fail during a mmap operation on a PCI P2PDMA memory region. This can happen under memory pressure or other conditions that prevent page insertion. The attacker needs the ability to mmap a P2PDMA device, which typically requires access to the device (e.g., through device file permissions). No additional privileges are required beyond that access [1].
Impact
When the vulnerability is triggered, memunmap_pages() hangs indefinitely upon removal of the PCI device. This results in a denial of service (DoS) as the device cannot be cleanly removed, potentially affecting system stability and availability [1].
Mitigation
The fix is included in Linux kernel stable commit e19cce88ec4c (see reference [1]). Users should update their kernel to a version containing this commit. No workaround is available; the only mitigation is to apply the patch [1].
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10a1f4dc72efc3PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4a2fc7ab42c349..218c1f5252b660 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
6220694c52a5PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4a2fc7ab42c349..218c1f5252b660 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
e19cce88ec4cPCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 78e108e47254ab..5497ce0be7c5c8 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
51b7181cfbedPCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 52e1564eadd0ba..ec53b2b0d57fe1 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -143,6 +143,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, virt_to_page(kaddr)); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
baa42b756d18PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 0f1e431bbfc20a..f97ac18a8dc8fc 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -143,6 +143,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, virt_to_page(kaddr)); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
baa42b756d18PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 0f1e431bbfc20a..f97ac18a8dc8fc 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -143,6 +143,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, virt_to_page(kaddr)); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
a1f4dc72efc3PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4a2fc7ab42c349..218c1f5252b660 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
6220694c52a5PCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4a2fc7ab42c349..218c1f5252b660 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
51b7181cfbedPCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 52e1564eadd0ba..ec53b2b0d57fe1 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -143,6 +143,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, virt_to_page(kaddr)); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
e19cce88ec4cPCI/P2PDMA: Release per-CPU pgmap ref when vm_insert_page() fails
1 file changed · +1 −1
drivers/pci/p2pdma.c+1 −1 modifieddiff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 78e108e47254ab..5497ce0be7c5c8 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -152,6 +152,7 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing percpu_ref_put() in the error path of p2pmem_alloc_mmap() when vm_insert_page() fails, causing a reference-count leak that hangs device removal."
Attack vector
An attacker with the ability to trigger a memory-mapping operation on a PCI P2PDMA device through sysfs (e.g., by writing to a sysfs file that invokes `p2pmem_alloc_mmap()`) can cause `vm_insert_page()` to fail. When this failure occurs, the function returns an error without calling `percpu_ref_put()`, leaving the per-CPU reference count elevated. This prevents `memunmap_pages()` from completing during device removal, causing the kernel to hang indefinitely. No special privileges beyond local access to the sysfs interface are required.
Affected code
The vulnerability is in the `p2pmem_alloc_mmap()` function in `drivers/pci/p2pdma.c` [patch_id=2661741]. The error path after a failed `vm_insert_page()` call frees the pool allocation via `gen_pool_free()` but does not release the per-CPU reference count (`percpu_ref`) that was acquired earlier for the pgmap.
What the fix does
The patch adds a single line — `percpu_ref_put(ref);` — inside the error-handling block of `p2pmem_alloc_mmap()` after `vm_insert_page()` fails [patch_id=2661741]. Previously, the error path called `gen_pool_free()` to return the allocated memory to the pool but did not decrement the per-CPU reference count (`ref`) that was acquired earlier via `gen_pool_alloc_owner()`. Without this decrement, the reference count remains elevated, causing `memunmap_pages()` to wait forever for the refcount to reach zero when the PCI device is removed. The fix ensures proper reference-count symmetry on the error path.
Preconditions
- inputAttacker must have local access to the sysfs interface that triggers p2pmem_alloc_mmap() on a PCI P2PDMA device
- inputvm_insert_page() must fail (e.g., due to memory pressure or invalid VMA state)
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/51b7181cfbedf289ce794b6d97a1c596c309ec38nvd
- git.kernel.org/stable/c/6220694c52a5a04102b48109e4f24e958b559bd3nvd
- git.kernel.org/stable/c/a1f4dc72efc3204db95d052058d785cad7ce755fnvd
- git.kernel.org/stable/c/baa42b756d183a59572f3890981a3d32b8d05d40nvd
- git.kernel.org/stable/c/e19cce88ec4c4877f4ff2469099b9cf23cc3e93envd
News mentions
0No linked articles in our index yet.