A critical use-after-free vulnerability was found and fixed in the Linux Kernel’s SPI subsystem, tracked as CVE-2021-46959. This long read aims to break down the bug, show you how it happened, its potential exploitation, and how it was fixed — all in plain language, with code snippets and links to the original sources.
What is SPI and Why Does it Matter?
SPI, or Serial Peripheral Interface, is a common way for computers and microcontrollers to talk to simpler devices like sensors, screens, and other chips. In Linux, the SPI core code juggles these connections through objects called “controllers.”
The Bug: What Went Wrong?
When registering SPI controllers (masters or slaves), drivers can use special “dev-managed” functions like devm_spi_alloc_master() instead of manual allocation. These rely on kernel memory management helpers (devres) to clean up safely when the device goes away.
Here’s the problem: during controller teardown with spi_unregister_controller(), the clean-up process was mistakenly peeking at the devres list after it had already been destroyed. This confusion happened because the list is freed earlier, so any code walking it is now handling garbage or missing information.
The result? Some controllers got tagged as “legacy” instead of “devm-managed,” causing their reference counters (refcount) to be dropped more than they should — down past zero. Dropping refcounts this way shakes kernel memory integrity and can let attackers corrupt internals or even run malicious code.
Here’s the scary warning you might see in your kernel logs
------------[ cut here ]------------
WARNING: CPU: 1 PID: 660 at lib/refcount.c:28 refcount_warn_saturate+x108/x174
[<b0396f04>] (refcount_warn_saturate) from [<b03c56a4>] (kobject_put+x90/x98)
[<b03c5614>] (kobject_put) from [<b0447b4c>] (put_device+x20/x24)
r4:b670014
[<b0447b2c>] (put_device) from [<b07515e8>] (devm_spi_release_controller+x3c/x40)
[<b07515ac>] (devm_spi_release_controller) from [<b045343c>] (release_nodes+x84/xc4)
r5:b670018 r4:b670010
[<b04533b8>] (release_nodes) from [<b045416>] (devres_release_all+x5c/x60)
...
This trace shows the dangerous path through kernel code where refcounts go wrong!
This is a simplified summary of what the buggy clean-up logic did
// Bad idea: checking devres after teardown
static void spi_unregister_controller(struct spi_controller *ctlr) {
// This part is unsafe after devres is destroyed!
if (devres_find(&ctlr->dev, devm_spi_release_controller, NULL, NULL)) {
// treat as devm-managed controller
} else {
// treat as legacy, decrement refcount here
}
}
But because the devres list was already gone by the time this ran, every controller was treated as "legacy," making the kernel "put" the device twice. That’s how the refcount bug and use-after-free happened!
The Fix
Instead of peeking through the destroyed devres list, a flag is added directly to the controller struct. This flag, set at allocation time, tells the kernel if this SPI controller is devm-managed or not — and it's not vulnerable to races, memory tear-down, or misidentification.
Fixed Pseudocode
struct spi_controller {
// ... lots of stuff ...
bool devm_allocated; // new flag!
};
struct spi_controller *devm_spi_alloc_master(...) {
struct spi_controller *ctlr = ...;
ctlr->devm_allocated = true;
...
}
static void spi_unregister_controller(struct spi_controller *ctlr) {
if (ctlr->devm_allocated) {
// clean up, but don't double free
} else {
// legacy clean up path
}
}
By setting and checking this flag, the kernel always knows exactly how to handle the clean-up, making future reference counter logic safe!
Exploit Potential
Who is affected? This bug mostly affects device drivers and systems allowing userland to add/remove SPI controllers dynamically. However, a local attacker with the ability to trigger SPI device lifecycle events might use this as an elevation vector:
Double-free can be abused for arbitrary code execution in kernel space.
- Refcount overflow lets attackers manipulate memory or trick the kernel into operating on freed objects — a classic bug class in security research.
Since privilege is needed to load new drivers or manipulate hardware entries, this is most pressing for system integrators, kernel developers, and embedded engineers who accept user-supplied hardware or dynamic hardware configurations.
References, Links, and Patches
- Linux kernel commit that fixed this issue
- CVE record on NVD
- kernel.org: spi: Fix use-after-free with devm_spi_alloc_* (mailing list)
- Simple kernel devm managed resource docs
Devres logic can’t be trusted after teardown — use in-struct flags or state variables.
- Refcount errors in kernel code are dangerous — always audit teardown paths, especially with dynamic hardware attachment/removal.
- Patch now! If you maintain a custom or downstream kernel, cherry-pick the fix ASAP.
Kernel bugs like this often linger for years — be vigilant, keep your systems up to date, and always mind your memory management!
*Written for developers, sysadmins, and security curious by an AI with a keen eye for kernel bugs. If you’re patching your systems or writing code that uses SPI in Linux, make sure you’re on a kernel version that’s safe from CVE-2021-46959!*
Timeline
Published on: 02/29/2024 23:15:07 UTC
Last modified on: 12/10/2024 17:55:18 UTC