A recently resolved Linux kernel vulnerability, CVE-2024-39484, quietly exposes the crucial role of cleanup functions in the reliability of device drivers. Even minor looking implementation choices, like compiler directives, can have unexpected security and stability consequences. In this post, we’ll walk through:
and how it was fixed.
If you work with Linux drivers, embedded hardware, or are just security curious, let's break it down!
Background: What’s the “Davinci MMC” Driver?
The Davinci family from Texas Instruments powers professional and industrial equipment. The davinci_mmc driver in the Linux kernel supports the MMC/SD controllers on these chips. Like most drivers, it needs to:
What Happened: The __exit Mess
Here’s the big detail: In the driver code, the “remove” function (_called when the driver is stopped or unregistered_) was declared like this:
static void __exit davinci_mmcsd_remove(struct platform_device *pdev)
{
// cleanup code here...
}
The __exit macro tells the kernel:
"You can discard this function entirely unless this driver is a loadable module."
- If the driver is _built into the kernel_ (CONFIG_MMC_DAVINCI=y), the function may never be available at runtime.
What’s the actual bug?
If the driver is builtin, and a device is removed (via sysfs, hotplug, or other triggers), the remove callback is gone – literally missing!
That means:
Eventually the driver or the system can become unreliable.
The remove function isn't just for modules! Devices can go away at runtime, even with builtin drivers.
Vulnerable code
// drivers/mmc/host/davinci_mmc.c
static void __exit davinci_mmcsd_remove(struct platform_device *pdev) {
// cleanup logic here ...
}
...
static struct platform_driver davinci_mmcsd_driver = {
.remove = davinci_mmcsd_remove,
};
Problem: With CONFIG_MMC_DAVINCI=y, the function is discarded after kernel boot.
Fixed code
// Remove the __exit annotation!
static void davinci_mmcsd_remove(struct platform_device *pdev) {
// cleanup logic here ...
}
...
static struct platform_driver davinci_mmcsd_driver = {
.remove = davinci_mmcsd_remove,
};
Now, the function is always available and cleanup always happens, no matter if the driver is built-in or a module.
Real-World Effects
- Resource leaks: Memory, DMA, or IRQ resources may be left claimed (invisible until things break).
- System instability: Later driver reloads might fail or misbehave due to “leftover” resources.
- Potential privilege escalation: While this bug isn’t a direct local root, leaking resources can sometimes be abused by attackers for denial of service or even for privilege escalation in delicately crafted circumstances.
There aren’t any known stable exploits for privilege escalation, but you _can_ trigger the leak
# As root. Replace 'davinci-mmcsd.' with your actual device.
echo -n "unbind" > /sys/bus/platform/drivers/davinci_mmcsd/unbind
# Now, check resource status—cleanup may NOT have run!
On a vulnerable kernel, unbinding like this won’t trigger the cleanup (remove) logic—memory, IRQs, etc, are still tied up.
How Was It Noticed?
The Linux kernel's own modpost tool issued warnings about the section mismatch:
WARNING: modpost: drivers/mmc/host/davinci_mmc: section mismatch in reference: davinci_mmcsd_driver+x10 (section: .data) -> davinci_mmcsd_remove (section: .exit.text)
This pointed out that the driver struct refers to a function that may have been thrown away entirely during build, depending on config.
The Patch
See the kernel commit that fixed this issue:
- Patch: mmc: davinci: Don't strip remove function when driver is builtin
References:
- CVE-2024-39484 at cve.org
- Linux Kernel Mailing List Patch Discussion
Best Practices Learned
- Don’t use __exit for remove() in drivers unless you really mean it. Devices can go away even with built-in drivers.
- Always test hardware hotplug (and removal!) paths, not just loading/unloading modules.
Summary
CVE-2024-39484 is a great example of how defensive programming and attention to detail matter in low-level code. A single careless use of __exit can lead to lasting system-wide resource leaks, with all the risks that brings.
If you run (or ship!) Linux-based systems with custom drivers, make sure you consistently test remove/unbind paths. The fix here was simple—but skipping it would have left hundreds of devices quietly getting unreliable over time.
*Stay safe! Check your configs, patch when you can, and always mind those kernel warnings.*
Timeline
Published on: 07/05/2024 07:15:10 UTC
Last modified on: 07/15/2024 06:50:20 UTC