Feb 18, 2026·6 min read·3 visits
The NVIDIA Tegra DRM driver forgot to release PID references after using them. Local attackers with access to the GPU render node can trigger infinite kernel memory leaks, eventually causing a Denial of Service (DoS) by exhausting the slab allocator.
A resource leak vulnerability in the Linux Kernel's `drm/tegra` subsystem allows local users to exhaust kernel memory by creating zombie PID references. The flaw stems from a missing `put_pid()` call in the interaction between `get_task_pid()` and `host1x_memory_context_alloc()`, turning the Tegra driver into a black hole for process structures.
Welcome to the Linux Kernel, where memory management is manual and forgetting to take out the trash can bring down an entire system. Today's subject is the drm/tegra subsystem, the Direct Rendering Manager driver for NVIDIA's Tegra System-on-Chip (SoC) series. These chips power everything from embedded AI boxes to automotive dashboards and the Nintendo Switch. In the world of kernel drivers, managing the lifecycle of objects is critical. When a driver needs to track a user-space process, it often grabs a reference to that process's PID structure.
In C, unlike managed languages like Python or Java, there is no garbage collector to sweep up your mess. If you grab a reference to an object, you are contractually obligated to release it. It's the "pottery barn rule" of kernel development: if you touch it, you probably own it until you say otherwise. Reference counting is the mechanism that keeps the kernel sanity intact.
CVE-2025-68233 is a classic tale of "I thought you were watching the kids." One function grabbed a reference, passed it to another, and assumed the recipient would handle the cleanup. The recipient, however, just borrowed it and walked away. The result? A struct pid that lives forever, occupying kernel memory long after the actual process has died—a zombie in the truest sense.
To understand this bug, you have to understand how Linux tracks processes. The kernel uses struct pid to represent a PID. Because PIDs are shared resources (used by signals, file descriptors, and drivers), they are reference-counted. The function get_task_pid() is used to acquire a reference. Internally, this increments an atomic counter. As long as that counter is greater than zero, the kernel cannot free the memory associated with that structure.
The vulnerability resides in the interaction between a caller function in the Tegra DRM driver and host1x_memory_context_alloc(). The code logic went something like this:
get_task_pid(current, PIDTYPE_PID) to get a handle on the current process.host1x_memory_context_alloc(...) to associate a memory context with that process.host1x_memory_context_alloc took ownership of the reference.Here is the punchline: host1x_memory_context_alloc did not take ownership. It used the PID to set up some context mapping and then returned. The caller, assuming the reference was consumed, never called put_pid(). This is a resource leak. Every time this code path is triggered (likely via a specific IOCTL), the reference count on that struct pid increments. Even if the user process terminates, the struct pid remains pinned in the slab allocator because the kernel thinks the Tegra driver is still using it.
Let's look at the smoking gun. The fix is embarrassingly simple, which highlights just how easy it is to miss these things during code review. The patch was applied to drivers/gpu/drm/tegra/uapi.c (or similar call site depending on the specific kernel version structure).
The Vulnerable Pattern:
// Get a reference to the current task's PID
struct pid *pid = get_task_pid(current, PIDTYPE_PID);
// Allocate the context.
// DEVELOPER ASSUMPTION: This function consumes 'pid'.
err = host1x_memory_context_alloc(client->host, dev, pid);
if (err < 0)
goto error;
// ... execution continues, 'pid' is never put.The Fix (Commit 6b572e5154af08ee13f8d2673e86f83bc5ff86cd):
struct pid *pid = get_task_pid(current, PIDTYPE_PID);
err = host1x_memory_context_alloc(client->host, dev, pid);
// FIX: We must release our reference to the PID here.
// host1x_memory_context_alloc does not take ownership.
put_pid(pid);
if (err < 0)
goto error;It is literally one line of code: put_pid(pid). Without this line, the struct pid leaks. While a single struct pid is small (a few hundred bytes), the accumulation causes fragmentation in the kernel's slab allocator (kmalloc caches). Furthermore, if the system has limits on the number of PID structures (not just the numerical PID value, but the backing memory), this can lead to an inability to spawn new processes or allocate kernel objects.
Exploiting this is trivial but tedious. It's not a flashy RCE where you pop a shell; it's a Denial of Service (DoS) where you choke the system until it turns blue. The attack requires access to the DRM device, typically /dev/dri/renderD128 or /dev/dri/card0. On most desktop and embedded Linux systems, any user in the video or render group has this access. This means a compromised low-privilege service or a local user can trigger it.
The Attack Chain:
host1x_memory_context_alloc path. This creates a new context—and leaks one PID reference.INT_MAX.struct pid or general kmalloc pools becomes exhausted. The OOM killer might trigger, but it can't free the leaked structures because the kernel still thinks they are in use.The beauty of this attack is its stealth. It looks like legitimate heavy usage of the GPU until the system suddenly freezes.
Why does this matter? Tegra chips are frequently used in "set-and-forget" devices: automotive infotainment, medical imaging displays, and edge AI boxes. These systems often run for months or years without rebooting. A slow memory leak here is a ticking time bomb. It might take a week or a month, but eventually, the device will become unstable and crash.
From a security perspective, this is a local DoS. In a multi-tenant environment (like a shared compute cluster using Jetson boards), one malicious user can bring down the node for everyone else. While it scores low on the CVSS scale (usually around 5.5 for local DoS), the operational impact on mission-critical embedded systems is high. You don't want your car's dashboard rebooting because a background process leaked too many PIDs.
Additionally, this highlights a weakness in code auditability. The mismatch between "caller frees" and "callee frees" semantics is a perennial source of bugs in C. Unless clearly documented, host1x_memory_context_alloc is an ambiguity trap waiting to snare the next developer who touches this code.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Linux Kernel Linux | >= 6.0, < 6.1.159 | 6.1.159 |
Linux Kernel Linux | >= 6.2, < 6.6.118 | 6.6.118 |
Linux Kernel Linux | >= 6.7, < 6.12.60 | 6.12.60 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-401 (Memory Leak) |
| Attack Vector | Local (IOCTL) |
| CVSS v3.1 (Est) | 5.5 (Medium) |
| EPSS Score | 0.00037 |
| Kernel Subsystem | drm/tegra |
| Exploit Maturity | PoC Possible (Trivial) |
Improper Release of Memory Before Removing Last Reference ('Memory Leak')