This report analyzes a use-after-free (UAF) vulnerability discovered in the IOCTL handler of a widely deployed graphics driver. We discuss the primitive, the race condition timing, and the challenges of heap exploitation on modern SLUB-based kernels. Our analysis reveals that high-concurrency buffer management remains a significant attack surface despite modern synchronization primitives.
The Vulnerability
The vulnerability lies in the GFX_IOCTL_DISPOSE handler. When a user requests the disposal of a mapped buffer object, the kernel fails to adequately synchronize access to the buffer’s reference count in high-contention scenarios.1 IOCTL handlers remain the primary vector for local privilege escalation.
“The complexity of managing shared state in monolithic kernels often leads to race conditions where the lifetime of an object is shorter than its last reference.” — Kernel Security Review
Specifically, the transition from 1 -> 0 in the reference counter is not atomic relative to the object’s presence in the global buffer table. An attacker can trigger a race where two threads simultaneously decrement the counter.
Detailed Root Cause Analysis
Tracing the execution into gfx_internal_free() reveals that the object lookup and reference decrement are split across a lock boundary.2 This architectural choice favors scalability over safety in GPU management.
// drivers/gpu/gfx/core.c (Simplified)
void gfx_dispose_buffer(int handle) {
mutex_lock(&table_lock);
struct gfx_buf *buf = table_lookup(handle);
mutex_unlock(&table_lock);
// [!] CRITICAL RACE WINDOW START
if (kref_put(&buf->ref, gfx_free_callback)) {
// gfx_free_callback invokes kfree(buf)
}
}
The fix involves ensuring the object is removed from the lookup table before the final reference is dropped. Without this, an attacker can re-lookup the buffer after it’s been queued for deletion.
Exploitation: Heap Reclamation
Exploitation on modern kernels requires reclaiming the freed slab with a controlled structure.3 Cross-cache attacks were considered but found unnecessary due to slab alignment.
By overlapping a gfx_buf (freed) with a msg_msg (reclaimed), we can overwrite the msg_msg->next pointer. This allows for a “limited arbitrary read” primitive, which we then use to locate the kernel base address and bypass KASLR.
Mitigation and Conclusion
The vulnerability was patched by consolidating the lookup and removal logic into a single atomic transaction. Future research should focus on automated detection of lock-split reference count decrements in driver codebases.