In early 2021, researchers and Linux kernel developers identified and patched a serious vulnerability (now tracked as CVE-2021-46973) in the Linux kernel's net/qrtr subsystem, specifically relating to how the Qualcomm MHI protocol handled socket buffer (skb) transmission. For administrators, kernel developers, or interested security researchers, understanding CVE-2021-46973's nature is essential for both secure deployment and vulnerability research. Let’s break down this vulnerability, walk through the core code paths, and explain why it existed and how it could be exploited.

What is the QRTR MHI Vulnerability?

The Linux kernel includes drivers for the QualComm MSM Interface (MHI), which is used for communication with peripheral chips (like modems). The QRTR (Qualcomm IPC Router) protocol uses MHI for sending packets.

In the vulnerable code, when a packet’s skb (socket buffer) is queued for MHI transmission, it’s possible for MHI’s ul_callback function to fire immediately—before kernel code finishes its own housekeeping. In particular:

- The callback may free the skb and, if it decrements the last reference to the associated socket (sk), free that as well.
- However, the vulnerable code would defer incrementing the sk refcount or handling skb until after the queue operation—putting it at risk of *use after free*.
- An attacker could potentially trigger these race conditions, causing memory corruption or, in theory, privilege escalation.

The Problem Code: How the Use-After-Free Happened

Before the patch, the code looked something like this (full context here). Condensed, the sequence was:

net/qrtr/mhi.c

static int qrtr_mhi_send(struct qrtr_sock *ipc, struct sk_buff *skb)
{
    /* ... */
    /* vulnerable: queueing first, then touching skb and sk */
    mhi_queue_skb(dev, skb, ...); // MHI gets the skb, may fire callback

    sk = skb->sk;
    sock_hold(sk);  // Might be too late: the callback could've already dropped the last ref!
    /* ... */
}

If MHI's upper layer callback fired instantly—before the thread runs sock_hold(sk)—the references could be prematurely decremented or the memory could be freed, leaving the rest of the kernel handling a *dangling pointer*.

How Was It Fixed?

The patch (full diff here) simply swapped the order of the operations: grab references and handle the pointers *before* queueing.

static int qrtr_mhi_send(struct qrtr_sock *ipc, struct sk_buff *skb)
{
    struct sock *sk = skb->sk;

    /* Correct: hold reference before queueing */
    sock_hold(sk);

    /* Now queue the skb for transmission */
    mhi_queue_skb(dev, skb, ...);
    /* ... */
}

By holding the reference *before* passing skb to MHI, the kernel ensures nothing gets freed out from under it.

Exploit Details: Could Attackers Really Abuse This?

Yes, but with caveats. Exploiting race conditions in kernel code is tricky, but with enough tries—and if the attacker can control QRTR/MHI traffic (e.g., with root or special device access)—it’s possible to:

This could crash the kernel (DoS)

- Or, possibly, enable arbitrary write/read, depending on slab reuse (the area where recycled memory lands).

This type of bug is a hot target for privilege escalation, especially on devices where users can access peripherals or QRTR is exposed.

Testing Vulnerable and Patched Kernel Behavior

If you want to test this in a lab, here’s a toy example (you’ll need real devices for a live exploit):

// Pseudo-code: Simulate racing callback during mhi_queue_skb()
struct sk_buff *skb = alloc_skb(...);
skb->sk = sk; // Assume sk is set up and refcounted appropriately

// In a separate thread, simulate callback firing instantly
void mhi_ul_callback(struct sk_buff *skb) {
    if (skb)
        kfree_skb(skb); // Free the skb
}

// Vulnerable: queueing before refcount
mhi_queue_skb(dev, skb, ...); // Leads to mhi_ul_callback(skb), may free it!

// Now this could be use after free:
foo = skb->data; // UAF if callback fired!

Original Commit (fix):

- https://github.com/torvalds/linux/commit/3477bfc7f21e06251eab2aee80c956d3771766f
- Linux Kernel Source—net/qrtr
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/qrtr

CVE Entry:

- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-46973

Update your kernel. Any version newer than June 2021 is patched.

- Restrict user-level access to QRTR/MHI interfaces if possible.
- Audit other drivers—this use-after-free pattern crops up anywhere asynchronous callbacks and refcounts meet.

Conclusion

CVE-2021-46973 is a classic example of how subtle timing bugs in the kernel core can lead to risky vulnerabilities. While the fix was simple—just hold your references before sending buffers—these bugs underscore the need for extra caution wherever asynchronous driver code is involved.

For blue teams: Patch and move on.
For researchers: Keep an eye out for similar races!

If you're responsible for Linux device security, always keep your kernels updated and pay special attention to subsystems involving hardware protocols and async queues.


For more security writeups, follow reputable sources like the Linux Kernel Mailing List and Qualcomm Code Aurora Security Bulletins. Stay safe!

Timeline

Published on: 02/27/2024 19:04:07 UTC
Last modified on: 08/01/2024 13:42:29 UTC