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