In early 2021, a critical vulnerability was discovered in the Linux Kernel’s usb:mtu3 driver. Tracked as CVE-2021-46930, this bug is a use-after-free vulnerability caused by the mishandling of a fundamental Linux data structure called list_head. This post walks through the technical background, the root cause, an example exploit sketch, how the Linux community fixed the bug, and why everyone running affected systems should update.
What’s Affected?
Linux Kernel with the usb:mtu3 (MediaTek USB3.) driver is vulnerable if running on versions prior to the fix described in the references below. MTU3 is widely used in smartphones, embedded boards, and some laptops powered by MediaTek chipsets.
list_head and Initialization
The Linux kernel uses the list_head structure everywhere to manage lists (queues) of objects. Before using, you must initialize the list or strange things can happen.
In the vulnerable code, a list_head field in a structure wasn’t initialized. As a result, when code tried to remove the structure from the list (standard list_del operation), the kernel accessed a dangling pointer.
Linux Kernel Address Sanitizer (KASAN) flagged the bug
BUG: KASAN: use-after-free in __list_del_entry_valid+x34/xe4
Call trace:
...
__list_del_entry_valid+x34/xe4
mtu3_req_complete+x4c/x300 [mtu3]
mtu3_gadget_stop+x168/x448 [mtu3]
usb_gadget_unregister_driver+x204/x3a
unregister_gadget_item+x44/xa4
Translation: the kernel tried to access memory that was already freed, resulting in a crash or worse, a potential route for code execution.
List operation (such as list removal) accesses freed memory.
4. Attackers cooperate by reallocating memory (heap spray). If an attacker can allocate controlled data where the freed object was, they might control pointers and get kernel code execution.
Minimal Exploit (PoC Sketch)
Here’s a very simplified C code sketch to illustrate how uninitialized lists cause use-after-free. Exploiting this in the wild is *much* harder, but shows the principle:
struct my_obj {
struct list_head lh;
// ... other fields
};
// Demonstrate what happens if you forget INIT_LIST_HEAD
struct my_obj *o = kmalloc(sizeof(struct my_obj), GFP_KERNEL);
// BUG: forgot to call INIT_LIST_HEAD(&o->lh)
list_del(&o->lh); // Will access invalid pointers
kfree(o);
// Memory now up for grabs
// In real attack, would "heap spray" to control o->lh.prev/next
For the actual CVE, an attacker (maybe with limited device access) would
- Trigger USB gadget removal/init enough times
When device's USB framework calls list operations, the attacker controls what’s accessed
This could lead to kernel crash (DoS) or privilege escalation.
The Fix
Patch: usb: mtu3: fix list_head check warning
What Changed?
Developers added the missing initialization
// Before: (Vulnerable)
my_obj_alloc() {
struct my_obj *o = kzalloc(...);
// missing: INIT_LIST_HEAD(&o->lh);
...
}
// After: (Safe)
my_obj_alloc() {
struct my_obj *o = kzalloc(...);
INIT_LIST_HEAD(&o->lh); // Now safe!
...
}
References and Further Reading
- Linux Kernel Patch Commit
- NVD CVE-2021-46930 Record
- Linux list_head documentation
- USB Gadget Subsystem
Conclusion
CVE-2021-46930 is a subtle but highly dangerous bug in the Linux kernel’s USB driver. It shows how easily a simple coding mistake around basic data structures can open doors for attackers. The community patched this quickly, but if you use MediaTek USB3 hardware with Linux, make sure you have kernel updates from Summer 2021 or later.
Timeline
Published on: 02/27/2024 10:15:07 UTC
Last modified on: 04/10/2024 16:39:23 UTC