CVE-2025-68226: The SMB Zombie That Refused to Die
Jan 15, 2026·6 min read
Executive Summary (TL;DR)
Developers tried to fix a bug but missed a spot. In the Linux kernel's SMB client, a background worker thread responsible for invalidating cached directory handles was using a raw `kref_put` instead of the proper `close_cached_dir` wrapper. This oversight left a window for a Use-After-Free (UAF) condition, allowing local attackers to crash the system or potentially corrupt kernel memory.
A Use-After-Free vulnerability in the Linux Kernel's SMB client implementation caused by an incomplete backport of a previous security fix. The issue resides in the directory caching mechanism where a raw reference count decrement was used instead of a safe wrapper function.
The Hook: Speed Kills (Eventually)
In the world of networked file systems, latency is the enemy. Every time you ask a server for a file listing, there's a round-trip tax. To combat this, the Linux SMB client (cifs.ko) implements directory caching. It keeps a handle (FID - File ID) open and caches the directory contents locally. This mechanism, managed by cfids (Cached File IDs), makes browsing a remote share feel snappy, almost like it's local disk.
But here's the catch: maintaining state in a distributed system is like trying to hold water in your hands. You have to know exactly when the server changes something, when the network drops, or when the cache expires. The kernel uses a background worker, cfids_invalidation_worker, to clean up mess—handles that are no longer valid or need to be flushed.
This specific vulnerability lives in that cleanup crew. It's a classic story of complexity breeding insecurity. The developers built a robust mechanism to handle reference counting for these cached objects, but as we'll see, even the best mechanisms fail when you forget to use them in one specific corner of the code.
The Flaw: The Copy-Paste Curse
This vulnerability is a 'regression via backport,' which is just a polite way of saying someone screwed up the copy-paste job. A previous commit (bdb596ceb4b7) introduced a safer way to handle closing these cached directories. The developers realized that simply decrementing the reference count wasn't enough; they needed a wrapper function, close_cached_dir(), to handle the heavy lifting of state management and synchronization before letting the object die.
However, when this logic was backported to stable branches (or refactored), one call site was missed. Inside the cfids_invalidation_worker() function, the code was still doing things the old, dangerous way. It was calling kref_put() directly.
Here is the logic flaw: kref_put simply atomically decrements a counter. If that counter hits zero, it calls a release function to free the memory. But close_cached_dir does more—it likely handles list removal, locking, or notifying other subsystems that the FID is gone. By bypassing this wrapper, the worker thread could free the cfid structure while it was still logically 'alive' in other contexts, or bypass necessary locking mechanisms, leading to a race condition where the memory is freed but a pointer remains dangling.
The Code: The Smoking Gun
Let's look at the actual diff. This is often the most illuminating part of a UAF analysis. You don't need to be a kernel wizard to spot the difference; you just need to know that close_cached_dir is the 'safe' function and kref_put is the 'raw' function.
The vulnerable code in fs/smb/client/cached_dir.c (or similar path depending on kernel version) looked like this inside the invalidation loop:
/* Vulnerable Logic */
if (cfid->is_valid) {
cfid->is_valid = false;
/* ... logic to cancel work ... */
kref_put(&cfid->refcount, release_cfid); // <--- THE BUG
}The fix, applied in commit abd29b6e17a918fdd68352ce4813e167acc8727e, replaces the raw decrement with the proper teardown function:
/* Fixed Logic */
if (cfid->is_valid) {
cfid->is_valid = false;
/* ... logic to cancel work ... */
close_cached_dir(cfid); // <--- THE FIX
}It is painfully simple. close_cached_dir presumably wraps kref_put but adds necessary pre-conditions or locking. By calling kref_put directly, the invalidation worker was effectively pulling the rug out from under the rest of the SMB client subsystem.
The Exploit: Racing the Reaper
Exploiting this requires winning a race against the kernel's worker thread. The goal is to catch the kernel in that split second where the cfid refcount hits zero and the memory is freed, but before the system has fully realized the handle is gone.
Here is the theoretical attack flow:
- Setup: Mount an SMB share. Open a directory to trigger the creation of a
cfid(cached file ID). This places the object in memory and increments the refcount. - Trigger: We need to trigger the
cfids_invalidation_worker. This usually happens on network disconnects, timeouts, or specific server responses indicating the handle is invalid. A local attacker could force this by manipulating network interfaces or effectively 'timing out' the connection. - The Race: While the worker is running and hits that vulnerable
kref_put, we concurrently attempt to access that same directory handle from a user-space process.
If we time it right, the worker frees the memory, but our concurrent read operation (or close operation) tries to access a member of the cfid struct. The kernel accesses invalid memory, and boom.
The Impact: Denial of Service (Mostly)
Realistically, for 99% of users, this is a Denial of Service (DoS) vulnerability. Triggering the UAF will result in a kernel panic, crashing the machine. In enterprise environments running critical file servers or jump boxes mounting SMB shares, a reboot is bad news.
Could this lead to Privilege Escalation? Theoretically, yes. If an attacker can groom the heap (specifically the SLUB allocator) such that the freed cfid object is immediately overwritten by a payload object of the same size before the dangling pointer is used, they might be able to hijack control flow.
However, the cfid structure is relatively complex, and the race window is tight. Modern Linux kernels have significant mitigations against heap spraying and UAF exploitation (like CONFIG_SLAB_FREELIST_HARDENED). While not impossible, turning this from a crash into a root shell is a 'Hard Mode' challenge reserved for the elite exploit developers. For everyone else, it's just a blue screen (or black screen, this is Linux).
The Fix: Just Use The Wrapper
The mitigation is straightforward: apply the patch. The fix ensures that cfids_invalidation_worker uses close_cached_dir(cfid) instead of kref_put(). This ensures that all teardown logic is centralized and consistent.
If you cannot patch immediately, your options are limited. You could try to disable directory caching in the SMB mount options (actimeo=0 or similar flags might reduce the caching behavior, though strictly speaking, cfids might still be allocated). The only 100% effective workaround without patching is to not mount SMB shares from untrusted networks or allow untrusted local users to interact with SMB mounts.
This vulnerability serves as a reminder: Backports are dangerous. Just because code works in the mainline kernel doesn't mean it drops cleanly into an older stable tree. Context matters, and in C, context is often managed manually by tired developers.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Linux Kernel Linux | Affected versions vary by distro backport status | Commit abd29b6e17a918fdd68352ce4813e167acc8727e |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-416 |
| Attack Vector | Local |
| CVSS Score | 5.5 (Medium) |
| Impact | Denial of Service (DoS) |
| Exploit Status | None (No Public PoC) |
| Component | fs/smb/client (cifs.ko) |
MITRE ATT&CK Mapping
Use After Free
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.