CVE-2026-45955
Description
In the Linux kernel, the following vulnerability has been resolved:
md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
When llbitmap_suspend_timeout() times out waiting for percpu_ref to become zero, it returns -ETIMEDOUT without resurrecting the percpu_ref. The caller (md_llbitmap_daemon_fn) then continues to the next page without calling llbitmap_resume(), leaving the percpu_ref in a killed state permanently.
Fix this by resurrecting the percpu_ref before returning the error, ensuring the page control structure remains usable for subsequent operations.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A timeout in llbitmap_suspend_timeout() leaves percpu_ref killed permanently, causing a use-after-free or resource leak in Linux kernel md/llbitmap.
Vulnerability
In the Linux kernel's md/md-llbitmap subsystem, the function llbitmap_suspend_timeout() may return -ETIMEDOUT without calling percpu_ref_resurrect() when the wait for percpu_ref to become zero times out. This leaves the percpu_ref in a permanently killed state. The bug affects kernel versions prior to the commit 2446d099350185caeed19ab2c0270451a97296fb [1].
Exploitation
An attacker must be able to trigger a timeout in llbitmap_suspend_timeout(), for example by causing the percpu_ref count to remain nonzero through prolonged I/O operations or race conditions. When the timeout occurs, the function returns -ETIMEDOUT, and the caller md_llbitmap_daemon_fn proceeds to the next page without calling llbitmap_resume(). The percpu_ref is never resurrected, leaving the page control structure in an inconsistent state.
Impact
Once the percpu_ref is killed permanently, subsequent operations on the associated page control structure can result in use-after-free, resource leaks, or system instability. Depending on the context, an attacker may leverage this to escalate privileges or cause a denial of service.
Mitigation
The fix is included in Linux kernel stable commit 2446d099350185caeed19ab2c0270451a97296fb [1]. Users should apply this patch or update to a kernel version containing it. No workaround is available if the patch is not applied.
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
6095417d6b669md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1eb434306162a5..bcb6eae1c711f6 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1eb434306162a5..bcb6eae1c711f6 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
2446d0993501md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
d119bd2e1643md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
095417d6b669md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1eb434306162a5..bcb6eae1c711f6 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 1eb434306162a5..bcb6eae1c711f6 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
2446d0993501md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
d119bd2e1643md/md-llbitmap: fix percpu_ref not resurrected on suspend timeout
2 files changed · +6 −4
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
drivers/md/md-llbitmap.c+3 −2 modifieddiff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c index 9c1ade19b7741e..cd713a7dc27064 100644 --- a/drivers/md/md-llbitmap.c +++ b/drivers/md/md-llbitmap.c @@ -712,8 +712,10 @@ static int llbitmap_suspend_timeout(struct llbitmap *llbitmap, int page_idx) percpu_ref_kill(&pctl->active); if (!wait_event_timeout(pctl->wait, percpu_ref_is_zero(&pctl->active), - llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) + llbitmap->mddev->bitmap_info.daemon_sleep * HZ)) { + percpu_ref_resurrect(&pctl->active); return -ETIMEDOUT; + } return 0; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing percpu_ref_resurrect() call on timeout in llbitmap_suspend_timeout() leaves the percpu_ref permanently killed."
Attack vector
An attacker or system condition that causes the percpu_ref to not reach zero within the `daemon_sleep` timeout window triggers this bug. The function `llbitmap_suspend_timeout()` kills the percpu_ref and waits; if the timeout fires, the ref is left killed. The caller `md_llbitmap_daemon_fn` then continues to the next page without calling `llbitmap_resume()`, so the page control structure (`pctl`) becomes permanently unusable for subsequent operations. No special network path or authentication is required — the bug manifests during normal md bitmap daemon operation when I/O holds the ref count elevated longer than expected.
Affected code
The bug is in `drivers/md/md-llbitmap.c` in the function `llbitmap_suspend_timeout()` [patch_id=2661023]. The function calls `percpu_ref_kill(&pctl->active)` and then waits for the reference count to reach zero. If the wait times out, the function returned -ETIMEDOUT without calling `percpu_ref_resurrect()`, leaving the percpu_ref in a permanently killed state.
What the fix does
The patch adds a call to `percpu_ref_resurrect(&pctl->active)` inside the timeout branch, before returning -ETIMEDOUT [patch_id=2661023]. This ensures that when the wait for the percpu_ref to reach zero times out, the reference count is resurrected (reversing the earlier `percpu_ref_kill`), so the page control structure remains in a usable state. The caller can then continue processing subsequent pages without the ref being permanently killed.
Preconditions
- configThe md/llbitmap daemon must be running and processing bitmap pages
- inputThe percpu_ref for a page control structure must not reach zero within the daemon_sleep timeout window
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.