The Linux Kernel is renowned for its complexity and robust handling of hardware. But sometimes, even tiny bugs in obscure subsystems can have serious consequences. CVE-2021-46933 is a good example—impacting the USB FunctionFS (f_fs) gadget driver, this issue led to dangerous reference counter underflows and potential use-after-free bugs, which could ultimately compromise system stability and security.
This article shines a light on this subtle yet critical vulnerability: its origin, behavior, impact, how to exploit it, and how it was fixed.
What is CVE-2021-46933?
CVE-2021-46933 describes a vulnerability in the Linux kernel's USB gadget subsystem, specifically in the FunctionFS (f_fs) driver. This vulnerability involves the handling of eventfd objects used to notify userspace code about USB events.
Due to incorrect reference counting, eventfd_ctx_put() was being called multiple times under certain conditions, resulting in a refcount underflow—a classic case of the double-free/invalid memory access bug.
Affected Kernel Code
The bug lived in drivers/usb/gadget/function/f_fs.c, specifically where the FunctionFS code calls ffs_data_clear() function multiple times without proper cleanup logic.
/* Vulnerable Code Snippet */
static void ffs_data_clear(struct ffs_data *ffs)
{
if (ffs->epfiles) {
kfree(ffs->epfiles);
/* epfiles was not NULLed */
}
if (ffs->ffs_eventfd) {
eventfd_ctx_put(ffs->ffs_eventfd);
/* ffs_eventfd was not NULLed */
}
/* ... */
}
Where is This Called?
Both ffs_fs_kill_sb (on unmount) and ffs_ep_release (when userland closes EP—the USB endpoint zero) end up calling ffs_data_clear(). If an eventfd is associated, multiple calls could decrement its refcount below zero.
How to Trigger the Bug (Exploitation Steps)
Let's break down a typical exploit scenario (note: this is more of a local denial-of-service/race bug, but in a privileged userspace gadget context):
Userland Closes EP, triggering ffs_ep_release (and calling ffs_data_clear).
4. Later, Userland Unmounts the f_fs Filesystem (calling ffs_fs_kill_sb), again calling ffs_data_clear.
If this happens, the eventfd refcount underflows, and you see kernel warnings like
[1946.293094] refcount_t: underflow; use-after-free.
...
[1946.482067] [<c012350>] (__warn+xe8/x154)
[1946.535309] [<c04a948c>] (refcount_warn_saturate+x110/x15c)
[1946.548708] [<c04a937c>] (refcount_warn_saturate) from eventfd_ctx_put+x48/x74
You can trace every call using ftrace
modprobe usb_f_fs
echo ffs_data_clear > /sys/kernel/debug/tracing/set_ftrace_filter
echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# perform mount, setup/teardown, close gadgets, etc.
echo > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace
Example trace
smartcard-openp-436 ... ffs_data_clear <- ffs_data_closed
smartcard-openp-431 ... ffs_data_clear <- ffs_data_closed
smartcard-openp-431 ... ffs_data_clear <- ffs_data_put
Exploit Effects
- Refcount Underflow on eventfd: The kernel may now consider an eventfd to be "closed" while someone still uses it, leading to use-after-free and possible kernel oops or corruption.
- Denial of Service: A bug in this subsystem can panic or hang the kernel, especially on embedded devices (think: Raspberry Pi gadgets, USB-OTG cables, etc).
- Local Privesc (Theoretical): While no remote vector exists, kernel refcount bugs are notorious for being chained into more serious attacks if other local vulnerabilities exist.
The Fix
The upstream patch was simple but effective:
- Set ffs_eventfd to NULL after freeing, so calling ffs_data_clear() a second (or third) time stops after the first free.
Patched Code
static void ffs_data_clear(struct ffs_data *ffs)
{
if (ffs->epfiles) {
kfree(ffs->epfiles);
ffs->epfiles = NULL;
}
if (ffs->ffs_eventfd) {
eventfd_ctx_put(ffs->ffs_eventfd);
ffs->ffs_eventfd = NULL; // Prevent double-free
}
/* ... */
}
Commits
- Mainline Linux Fix (commit 6e81cbf86c2b02c5b8f5b06a7eac3de468bdb16e)
- Debian Security Advisory
Responsible Disclosure and Timeline
- Reported by: Community kernel contributors (see the Linux USB mailing list for ongoing discussions).
Conclusion: Why Such Bugs Matter
CVE-2021-46933 is classic—tiny error, big consequences. Even though the FunctionFS driver is mostly used with developer gadgets or embedded devices, this bug shows how convoluted reference counting in the kernel can be, and how dangerous double free/underflow can get.
If you are running a Linux system where gadgets are set up via f_fs and you use eventfd-based notifications, you must update your kernel to include this fix.
References
- Upstream Patch Commit
- Debian CVE Tracker
- Linux-USB Mailing List
- Ftrace Documentation
- Eventfd in the Linux Kernel
Timeline
Published on: 02/27/2024 10:15:07 UTC
Last modified on: 04/10/2024 18:36:47 UTC