CVE-2026-45885
Description
In the Linux kernel, the following vulnerability has been resolved:
power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
Using the devm_ variant for requesting IRQ _before_ the devm_ variant for allocating/registering the power_supply handle, means that the power_supply handle will be deallocated/unregistered _before_ the interrupt handler (since devm_ naturally deallocates in reverse allocation order). This means that during removal, there is a race condition where an interrupt can fire just _after_ the power_supply handle has been freed, *but* just _before_ the corresponding unregistration of the IRQ handler has run.
This will lead to the IRQ handler calling power_supply_changed() with a freed power_supply handle. Which usually crashes the system or otherwise silently corrupts the memory...
Note that there is a similar situation which can also happen during probe(); the possibility of an interrupt firing _before_ registering the power_supply handle. This would then lead to the nasty situation of using the power_supply handle *uninitialized* in power_supply_changed().
Fix this racy use-after-free by making sure the IRQ is requested _after_ the registration of the power_supply handle.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Use-after-free in Linux kernel's cpcap-battery driver due to devm_ IRQ request before power_supply registration, leading to race condition.
Vulnerability
In the Linux kernel's cpcap-battery driver, a use-after-free vulnerability exists due to incorrect ordering of devm_ resource allocations. The driver requests the interrupt handler using devm_request_irq() before allocating and registering the power_supply handle with devm_power_supply_register(). Because devm_ resources are freed in reverse allocation order, during driver removal the power_supply handle is deallocated before the interrupt handler is unregistered. This creates a race window where an interrupt can fire after the power_supply handle is freed but before the IRQ handler is removed. Additionally, during probe, an interrupt may fire before the power_supply handle is registered, leading to use of an uninitialized pointer. The vulnerability affects Linux kernel versions where the cpcap-battery driver uses this allocation order; the exact affected versions are not specified in the available references.
Exploitation
An attacker would need to be able to trigger an interrupt on the cpcap-battery device, which typically requires local access or the ability to influence battery events. The race condition occurs during driver removal (e.g., module unload or device unbind) or during probe. The attacker must time the interrupt to occur in the narrow window between the deallocation of the power_supply handle and the unregistration of the IRQ handler (or before registration during probe). No authentication is required beyond the ability to cause the interrupt.
Impact
Successful exploitation leads to a use-after-free condition where the interrupt handler calls power_supply_changed() with a freed or uninitialized power_supply pointer. This can cause a system crash (denial of service) or, in worst case, silent memory corruption that may be leveraged for privilege escalation. The impact is limited to systems using the Motorola cpcap battery driver, typically found in certain mobile devices.
Mitigation
The fix is to reorder the driver initialization so that the IRQ is requested after the power_supply handle is registered. This has been implemented in the Linux kernel stable tree via commits [1] and [2]. Users should update to a kernel version containing these commits. No workaround is available other than avoiding driver removal or probe while interrupts are possible. The vulnerability is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.
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
16642f33e34b96power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
e261be6f1892power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
2841bbb5a35cpower: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
f3fbe309c9bfpower: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 30ec76cdf34b0b..eaad9f53bb5ccb 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
cbb9b07f88a9power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 5dd76c0ac98dae..d84b81e7736287 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
2ce2334be155power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index d98d9244e39481..1dbd1c67d00a9a 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
3ff75cba1c98power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8d62d4241da3d2..053d3767004475 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1071,10 +1071,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1091,6 +1087,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
c549dd3de4b3power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 793d4ca52f8a19..e39f7f7414cb94 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -888,10 +888,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -919,6 +915,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
f3fbe309c9bfpower: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 30ec76cdf34b0b..eaad9f53bb5ccb 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
e261be6f1892power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
cbb9b07f88a9power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 5dd76c0ac98dae..d84b81e7736287 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
c549dd3de4b3power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 793d4ca52f8a19..e39f7f7414cb94 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -888,10 +888,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -919,6 +915,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
642f33e34b96power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
3ff75cba1c98power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8d62d4241da3d2..053d3767004475 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1071,10 +1071,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1091,6 +1087,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
2ce2334be155power: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index d98d9244e39481..1dbd1c67d00a9a 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
2841bbb5a35cpower: supply: cpcap-battery: Fix use-after-free in power_supply_changed()
1 file changed · +4 −5
drivers/power/supply/cpcap-battery.c+4 −5 modifieddiff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8106d1edcbc26a..507fdc1c866d56 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1122,10 +1122,6 @@ static int cpcap_battery_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - error = cpcap_battery_init_interrupts(pdev, ddata); - if (error) - return error; - error = cpcap_battery_init_iio(ddata); if (error) return error; @@ -1142,6 +1138,10 @@ static int cpcap_battery_probe(struct platform_device *pdev) return error; } + error = cpcap_battery_init_interrupts(pdev, ddata); + if (error) + return error; + atomic_set(&ddata->active, 1); error = cpcap_battery_calibrate(ddata); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Incorrect ordering of devm-managed resource allocation: IRQ handler was registered before the power_supply handle, causing a use-after-free race during driver removal and an uninitialized-use race during probe."
Attack vector
An attacker who can trigger a hardware interrupt on the cpcap-battery device (e.g., by physically manipulating the battery or through a connected charger) during driver removal can cause the interrupt handler to call `power_supply_changed()` on a freed `power_supply` handle [patch_id=2661685]. The same race exists during probe: an interrupt arriving before `power_supply` registration causes the handler to use an uninitialized handle. No special network or authentication is required; the precondition is that the kernel module is being loaded or unloaded while the hardware can generate interrupts.
Affected code
The vulnerability is in `drivers/power/supply/cpcap-battery.c` in the `cpcap_battery_probe()` function. The original code called `cpcap_battery_init_interrupts()` (which registers the IRQ handler via a `devm_` variant) _before_ the `power_supply` handle was allocated and registered. This ordering caused the `power_supply` handle to be freed before the IRQ handler during device removal, due to `devm_`'s reverse-allocation teardown order.
What the fix does
The patch moves the `cpcap_battery_init_interrupts()` call to _after_ the `power_supply` handle has been allocated and registered via `devm_power_supply_register()` [patch_id=2661685]. Because `devm_` resources are torn down in reverse allocation order, the IRQ handler is now unregistered _before_ the `power_supply` handle is freed, closing the use-after-free race during removal. The reorder also ensures that during probe, the `power_supply` handle is fully initialized before any interrupt can fire, preventing the uninitialized-use race.
Preconditions
- configThe cpcap-battery driver must be loaded (probe) or unloaded (remove) on a system with the Motorola CPCAP PMIC.
- inputThe hardware must be capable of generating interrupts (e.g., battery insertion/removal, charger attach/detach) during the probe or removal window.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- git.kernel.org/stable/c/2841bbb5a35c4449c0a0458e8e476b2a62f95147nvd
- git.kernel.org/stable/c/2ce2334be155bd8bad6377e99984246ce4dbd08cnvd
- git.kernel.org/stable/c/3ff75cba1c98349a23a8f9333981deba1972cc11nvd
- git.kernel.org/stable/c/642f33e34b969eedec334738fd5df95d2dc42742nvd
- git.kernel.org/stable/c/c549dd3de4b3f6e726d1b8386d40ccf7d3abdbe4nvd
- git.kernel.org/stable/c/cbb9b07f88a9ef6518934c41eb3e8cf840d657d5nvd
- git.kernel.org/stable/c/e261be6f18929f2397cd54cd583a2df624c129c1nvd
- git.kernel.org/stable/c/f3fbe309c9bfe1aac1e2b26543e9dc4829f3275anvd
News mentions
0No linked articles in our index yet.