Skip to content

Devils Are in the File Descriptors: It Is Time To Catch Them All

Le Wu

Blue TeamBlack Hat USA 202231 minAdvancedOpen on YouTube ↗

Reviews devils Are in the File Descriptors: It Is Time To Catch Them All and evaluates key implications for security practitioners.

Summary

Overview: A security researcher specializing in Android and Linux kernel auditing presents research on race condition vulnerabilities within Linux file descriptor handling. Drawing from discoveries including CVE-2021-0929, the speaker examines how file descriptors (FDs) shared across user space and kernel space can be manipulated concurrently. The presentation demonstrates how predictable file descriptor allocation primitives enable user-space threads to trigger use-after-free (UAF) vulnerabilities during kernel FD export and import operations.

Key Topics: - Linux kernel file descriptor table architecture and file object translation mechanics - The three-step FD export sequence and two-step FD import sequence - Concurrency race conditions between user-space FD operations and kernel-space file objects - Predictable file descriptor allocation and exploitation of kernel Use-After-Free (UAF) flaws (CVE-2021-0929)

Technical Details: In the Linux kernel, everything is represented as a file, and processes identify open files using integer file descriptors that index into a process-level FD array pointing to kernel file objects. The kernel frequently translates between user-space FDs and kernel file objects using export and import procedures. The export process occurs in three distinct steps: acquiring an unused integer FD, updating the process FD array so the integer points to the kernel file object, and returning the FD integer to user space. Conversely, the import process consists of two steps: querying the FD array with the integer to retrieve the file object, and incrementing the object's reference count. Closing an FD zeroes the FD array index, releases the object reference, and marks the integer as available.

The research was inspired by CVE-2021-0929, where thread B created a DMA-BUF file descriptor via the Android ION memory allocator and passed it to kernel space. In thread A, the kernel imported the DMA-BUF FD and generated an ION handle, mapping the memory buffer into kernel virtual address space. However, thread B concurrently triggered unmapping of that memory, creating a race condition and a kernel use-after-free when thread A subsequently referenced the virtual address. This highlighted two race models: thread A accessing a file object in kernel space while thread B operates on the FD in user space, and concurrent operations on the FD itself.

Exploiting these race conditions hinges on file descriptor predictability. Linux assigns FDs sequentially in ascending order (where 0, 1, and 2 are standard streams, leaving 3 as the first available FD) and deterministically reuses the lowest-numbered closed descriptor. As a result, an attacker running a parallel thread does not need to wait for step 3 (returning the FD to user space) to know the FD integer. Between step 2 (where APIs such as fd_install make the FD live in the FD array) and step 3, a race window exists. A concurrent user-space thread can immediately close the predicted FD, causing the kernel to decrement the reference count and free the underlying file object. When the primary kernel thread continues executing and accesses the object, a use-after-free occurs. Auditing reveals this bug pattern across Android vendor kernels and Linux mainline, particularly within DMA-BUF and graphics drivers.

Key Takeaways: Kernel developers must treat file descriptors as globally accessible to user-space threads immediately upon calling fd_install or equivalent binding functions. Code should never perform further dereferences or modifications on a file object or its associated resources after step 2 of the export operation. Security auditors can identify these vulnerabilities by tracking kernel APIs that publish file descriptors and checking for subsequent object access within the same execution path.

(AI summary — Gemini Flash)

Related talks