In June 2021, a subtle yet serious information leak was fixed in the Linux kernel. It was tracked as CVE-2021-46906, and impacted the USB Human Interface Device (HID) subsystem — particularly in the way HID control transfers handled buffer lengths. This post explains what happened, how it could be exploited, and how it was fixed. All in simple language.

Background: What Is the USB HID Subsystem?

Many devices — keyboards, mice, game controllers — use the USB HID subsystem in Linux. The kernel driver manages communication between user programs and the hardware.

An important job of the HID driver is submitting requests (known as "URBs") to the USB layer, including special "control" requests. Each request specifies a buffer and a length.

The Bug

The bug was in the function hid_submit_ctrl() inside the kernel's HID code. When a HID report had a size of zero, the calculation to figure out how much data to transfer went wrong. Instead of a correctly-sized buffer, it gave the USB core a huge buffer length (16384 bytes), filled with uninitialized kernel memory.

This memory could be sent over USB to a device or even accessible to user space, creating an "information leak." Malicious USB devices or, under some circumstances, attackers with access to the system could recover kernel memory contents — potentially leaking passwords, keys, or other sensitive data.

The affected code was in drivers/hid/usbhid/hid-core.c

int hid_submit_ctrl(struct hid_device *hid, unsigned dir, u8 *buffer, int report_type, int report_id)
{
    ...
    transfer_buffer_length = hid_report_len(report);
    ...
    // Submit the URB with this buffer and length.
}

The crucial calculation was in hid_report_len()

static int hid_report_len(struct hid_report *report)
{
    unsigned int n = report->size;
    int size = n / 8;
    if (n % 8)
        size++;

    return size;
}

If report->size was zero, both size and transfer_buffer_length ended up zero — but because of missing checks, the logic that followed could still submit a gigantic buffer.

How It Was Discovered

The bug was found with syzkaller, a fuzzing tool that generates random syscalls to test kernel code. Using a crafted report of size zero, syzkaller triggered this issue and KMSAN, a kernel memory sanitizer, reported a huge info leak.

Attacker controls a USB device: They plug a custom device into your PC.

- Attacker can run code on your system: They can open the /dev/hidraw device, or use other interfaces.

USB core passes (large) buffer out, filled with uninitialized bytes from kernel memory.

4. Attacker receives the buffer over USB, reads data that can include kernel stack/heap, potentially leaking usernames, passwords, ssh keys, or even kernel pointers.

Example Pseudocode (Simulated for Education)

// Assume report->size == 
size_t len = hid_report_len(report); // Returns , but mishandling results in extra large buffer sent

// ...Later, transfer_buffer_length is wrong
usb_submit_urb(buffer, transfer_buffer_length); // transfer_buffer_length = 16384

// Buffer sent to device – attacker reads sensitive data!

The Patch: Safer Calculation with DIV_ROUND_UP

The fix made sure zero-sized reports always resulted in a transfer length of zero using the DIV_ROUND_UP macro, which safely divides and rounds up:

Before

int size = n / 8;
if (n % 8)
    size++;

After

int size = DIV_ROUND_UP(n, 8);
// Now if n is zero, size is zero!

The DIV_ROUND_UP macro is defined as

#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))

Now, if report->size is zero, hid_report_len() safely returns zero and no huge buffer will be submitted.

References

- CVE-2021-46906 at MITRE
- Patch Commit: “HID: usbhid: fix info leak in hid_submit_ctrl”
- Syzkaller Fuzzer
- Linux HID subsystem documentation

Who’s Affected and How To Fix It

All major Linux distributions between kernel versions prior to the 5.13 cycle. If you use USB HID devices on servers or desktops, especially untrusted ones, update your kernel now. Most distros have shipped the patch.

Tools like syzkaller and KMSAN are essential in catching such vulnerabilities.

Feel free to share this post, and stay secure — patch your systems!

Timeline

Published on: 02/26/2024 18:15:07 UTC
Last modified on: 04/17/2024 17:28:34 UTC