CVE-2021-46969 - Linux Kernel MHI Core Queue Error Handling Vulnerability Explained
In early 2021, the Linux kernel community patched a subtle but important bug identified as CVE-2021-46969. This vulnerability affected the MHI (Modem Host Interface) which is pivotal for modems, particularly on systems like Android phones and connected laptops using Qualcomm chipsets.
The bug, located in the mhi_queue function of the MHI core, could cause invalid error returns and a dangerous use-after-free memory issue, both of which potentially expose a system to stability problems and hard-to-debug kernel crashes.
This post explains the problem, illustrates with real kernel code, and guides you through what was fixed and why it matters.
What is MHI and Why Does It Matter?
MHI stands for Modem Host Interface and allows communication between the operating system and modem hardware (for WiFi, LTE, 5G, etc.). It’s widely used in embedded and mobile Linux.
The Linux kernel module that manages this is responsible for queuing data traffic between the host and the modem using a function named mhi_queue.
What Went Wrong?
The function mhi_queue() is supposed to queue packets for transmission to the modem. However, when the modem was not in the _M_ ("Mission Mode") state—meaning it was sleeping or suspended (in states like _M3_)—the function sometimes returned an error indicating the "doorbell" (a kind of signal line to the modem) wasn’t accessible.
The problem:
This error return was not really an error — it just meant the modem wasn’t ready _yet_. Higher layers in the kernel reacted by freeing the packet buffer (skb), believing the queue had failed, but in reality, the packet had already been processed for queuing. This caused a *use-after-free* bug: the kernel could later access already-freed memory, leading to unpredictable crashes or security problems.
Unstable Devices: Systems might randomly reboot or freeze on network activity.
- Potential Exploit: Malicious software could potentially use this bug for privilege escalation or to crash the system in a denial-of-service scenario.
Here’s a simplified version of the code before the fix
int mhi_queue(struct mhi_device *mhi_dev, struct sk_buff *skb, ...)
{
/* ... */
if (doorbell_not_accessible(mhi_ctrl)) {
/* Not really an error, but returns failure */
return -EIO;
}
/* Queueing code ... */
}
Higher level code thinks mhi_queue failed and might do
ret = mhi_queue(dev, skb, ...);
if (ret) {
/* Assume skb wasn't used, so free it */
kfree_skb(skb);
}
But skb was already associated with the modem—leading to a *double free* or *use-after-free*.
The Fix
Developers realized the mistake: the inaccessibility of the doorbell during low-power states was already being handled asynchronously. Returning error here was misleading and problematic.
The corrected code now returns success for this case, not an error. It ensures the skb is queued properly, and the state will be updated once the modem wakes up.
The Patch (excerpt)
if (doorbell_not_accessible(mhi_ctrl)) {
/* Just don't ring, but return success.
* The wakeup process will ring the doorbell later.
*/
return ;
}
This also prevents higher-level functions from wrongly freeing the skb.
References & Original Sources
- CVE-2021-46969 NVD Entry
- Kernel Patch Email (LKML)
- Linux Kernel Source: drivers/bus/mhi/core/main.c
How To Stay Safe
- Update your kernel: This fix is present in Linux v5.14 and later. Most distributions patched this in their backports.
- If you maintain custom Android kernels or embedded systems with Qualcomm modems, check your codebase or vendor trees for this patch.
Closing
CVE-2021-46969 was a classic example of how a small logic error in core networking code can lead to big stability and security problems. The fix is simple but crucial: Don’t return error when your hardware is just taking a nap. Wait, and ring the bell when it wakes up.
Keep your systems patched and keep learning—kernel bugs are often a lesson in both programming and hardware realities!
If you want to see the exact patch yourself, read this kernel.org commit (replace with actual commit ID if available).
Timeline
Published on: 02/27/2024 19:04:07 UTC
Last modified on: 01/08/2025 17:19:50 UTC