CVE-2025-38236 is a Linux kernel use-after-free in the AF_UNIX stream-socket implementation. A low-privileged process can create a particular sequence of out-of-band sends and receives that leaves the socket’s oob_skb field pointing to an already freed socket buffer. A later receive operation with MSG_OOB then accesses that stale object.
The vulnerable entry point is local, but the security boundary is not minor. Google Project Zero demonstrated that the bug could be developed from native code execution inside a Linux Chrome renderer into kernel memory manipulation on an x86-64 Debian Trixie system. The research ultimately obtained writable access to arbitrary kernel memory and used that capability to alter the UTS data reported by uname. Chrome subsequently blocked renderer processes from sending MSG_OOB messages. (Project Zero)
That result does not make CVE-2025-38236 a remote, unauthenticated Linux exploit. An attacker normally needs a process executing code on the target host first. The important question is whether that process is supposed to remain constrained. Browser renderers, shared CI workers, containers, notebook services, plugin hosts, document processors, developer workstations, and multi-user systems all create situations in which untrusted or partially trusted code may run without being trusted with the kernel.
The official CVE record classifies the issue as a use-after-free, and NVD assigns a CVSS 3.1 base score of 7.8 with a local attack vector, low privileges, no user interaction, and high confidentiality, integrity, and availability impact. The defect is in net/unix/af_unix.c. (NVD)
CVE-2025-38236 at a Glance
| Property | Assessment |
|---|---|
| Affected component | Linux kernel AF_UNIX stream sockets |
| Vulnerable feature | Out-of-band data handling through MSG_OOB |
| Weakness | Use-after-free, CWE-416 |
| Primary vulnerable function | unix_stream_read_generic() and its interaction with OOB queue handling |
| Required starting position | Local low-privileged code execution |
| User interaction | None after attacker code is running |
| Direct network exposure | Not a conventional remotely reachable listening-service vulnerability |
| Potential impact | Kernel memory corruption, denial of service, privilege escalation, sandbox escape, or host compromise |
| Public technical evidence | Reproducible KASAN failure and a Project Zero kernel exploitation demonstration |
| Preferred remediation | Install the supported vendor kernel update, reboot into it, and verify the running build |
| Temporary hardening | Restrict MSG_OOB in untrusted sandboxes and disable AF_UNIX OOB in custom kernels that do not require it |
The compact trigger sequence can be expressed as several send() and recv() operations, but the number of system calls understates the complexity of the bug. The vulnerability depends on a specific receive-queue state involving multiple socket buffers, a pointer used to identify the active out-of-band buffer, and a cleanup path that does not preserve its intended ownership invariant.
The result is a useful example of a broader kernel-security rule: a feature does not need to be common to be reachable. If a sandbox allows a general-purpose system call but does not constrain its flags, the sandbox may accidentally expose uncommon kernel subsystems that its designers never intended the contained application to use.
Why a Local Kernel Bug Can Be a Critical Boundary Failure
“Local” describes the initial attack vector. It does not describe the final impact.
A remote attacker cannot ordinarily send a network packet to a Linux server and directly invoke this AF_UNIX receive sequence. The attacker first needs code execution in a process on that machine. That initial execution might come from a different vulnerability, a stolen account, a malicious build, an uploaded plugin, an untrusted notebook, a compromised package, or a browser exploit.
Many modern systems intentionally execute code that they do not fully trust:
| Environment | Expected low-privileged execution |
|---|---|
| Browser | Web content and renderer code execute under a sandbox |
| CI platform | Pull requests, build scripts, compilers, and tests execute as worker users |
| Container platform | Tenant workloads share the host kernel |
| Notebook service | Users execute Python, R, native extensions, and subprocesses |
| Plugin platform | Third-party extensions run inside a constrained host |
| Shared hosting | Customers receive separate Unix accounts or containers |
| Developer workstation | Browsers, package managers, IDE extensions, and local tools process external content |
| Security analysis system | Malware samples and hostile files may execute inside a sandbox |
The security promise in these environments is not that untrusted code will never execute. The promise is that the code will remain inside an assigned process, user account, container, or sandbox. A kernel privilege-escalation vulnerability attacks that second boundary.
Consider a compromised web service running as www-data. Without a kernel exploit, the attacker may be limited by filesystem permissions, process credentials, Linux security modules, namespaces, and container controls. With kernel-level write capability, those controls can become attacker-controlled state. Credentials can be altered, security hooks can be bypassed, other processes can be inspected, and host-level secrets may become reachable.
The Project Zero result gives this risk concrete meaning. The starting point was native code execution inside a Chrome renderer, not root access and not an unrestricted shell. The renderer sandbox still exposed enough Linux interfaces to trigger and develop the AF_UNIX bug. The research used the resulting memory-corruption primitives to gain writable access to kernel memory. (Project Zero)
A system that never executes untrusted local code may assign CVE-2025-38236 a lower immediate priority than a shared build server. It should not conclude that the defect is harmless. Initial-access vulnerabilities and kernel escalation vulnerabilities frequently form different stages of one attack chain.
AF_UNIX Stream Sockets and the MSG_OOB Path
Unix domain sockets provide local interprocess communication. Unlike an Internet socket, an AF_UNIX socket normally connects processes on the same operating system. Applications use them for service APIs, desktop buses, container-runtime interfaces, database connections, browser IPC, local proxies, logging, and many other tasks.
A stream-oriented Unix domain socket resembles a TCP stream from the application’s point of view. It provides an ordered sequence of bytes rather than preserving each application write as a distinct datagram. The implementation still needs internal objects to store data while it waits for the receiving process.
Linux represents queued socket data with socket buffers, commonly called SKBs after struct sk_buff. For a receiving AF_UNIX stream socket, those objects are linked into sk_receive_queue.
Out-of-band data adds another state dimension. When an application passes MSG_OOB to an appropriate send operation, it identifies urgent data that the receiver can retrieve separately from ordinary in-band bytes. Linux added OOB support for AF_UNIX stream sockets in the commit that landed with Linux 5.15. For this interface, the urgent payload is limited to one byte, and only one current OOB byte can be pending at a time. (Project Zero)
The receiving unix_sock structure maintains an oob_skb pointer. That pointer identifies the SKB containing the currently active urgent byte. It allows an OOB receive operation to locate the relevant buffer without walking and consuming the ordinary stream in its normal order.
This creates at least two views of the same object:
- The SKB is an element in the socket’s receive queue.
- The socket’s
oob_skbfield may point directly to that SKB.
That arrangement is safe only while the queue and the direct pointer agree about the object’s lifetime.
An SKB also carries bookkeeping information used by the owning subsystem. AF_UNIX stores a consumed count in the SKB control area. Project Zero describes the remaining length as the original SKB length minus this consumed value:
remaining = skb->len - UNIXCB(skb).consumed;
The kernel helper is effectively represented as unix_skb_len(skb). (Project Zero)
When an OOB byte is received through recv(..., MSG_OOB), the urgent byte is considered consumed. The SKB may nevertheless stay in the receive queue temporarily so that the ordinary stream path can preserve the OOB boundary behavior expected by the socket API. The SKB’s remaining length becomes zero, while the direct oob_skb pointer is normally cleared.
The queue may therefore contain an object that is still linked but has no unread payload:
| SKB state | In receive queue | Remaining length | Referenced by oob_skb | Expected treatment |
|---|---|---|---|---|
| Ordinary unread data | Yes | Greater than zero | No | Read in stream order |
| Active OOB data | Yes | Usually one byte | Yes | Available to OOB receive path |
| Consumed OOB marker | Yes temporarily | Zero | No | Skip or remove safely |
| Freed object | No | Invalid | Must be no | Never access |
| CVE-2025-38236 dangling state | No | Invalid | Yes | Use-after-free on later OOB access |
The last row is the violated invariant. Once the active OOB SKB has been freed, no socket field may continue to identify it as a live urgent-data object.
The Queue State That Triggers CVE-2025-38236
The official Linux announcement provides a compact reproducer that creates two consumed OOB SKBs, adds a third live OOB SKB, reads that third byte through the normal path, and then asks the OOB path to access it again. The announcement states that the final operation produces a KASAN slab-use-after-free in unix_stream_read_actor. (OpenWall Lists)
The important part is not the particular byte values. It is the queue topology.
After the first urgent byte has been sent and received as OOB data, the first SKB remains as a consumed marker:
SKB 1
remaining length = 0
not referenced by oob_skb
The same operation is repeated:
SKB 1
remaining length = 0
SKB 2
remaining length = 0
A third urgent byte is then sent but not yet received through the urgent-data path:
SKB 1
remaining length = 0
SKB 2
remaining length = 0
SKB 3
remaining length = 1
oob_skb points here
Project Zero documents this exact three-entry state: two zero-length consumed OOB SKBs followed by a one-byte SKB referenced by the OOB pointer. (Project Zero)
A normal receive operation now enters unix_stream_read_generic(). Its OOB-management logic sees the first zero-length SKB and advances. The surrounding receive logic then encounters the second zero-length entry. A loop associated with the socket peek offset assumes that the SKB length being processed will make forward progress. Because the second consumed OOB SKB also has a remaining length of zero, the code advances again and reaches the third SKB.
The third SKB is the live urgent-data object. The ordinary stream path reads and frees it. The expected OOB cleanup check does not run in the necessary way for that sequence, so oob_skb remains unchanged.
The queue after the free no longer contains SKB 3, but the direct pointer still holds its old address:
oob_skb ─────────────► freed SKB memory
The next recv(..., MSG_OOB) follows this pointer. The kernel treats the address as a live struct sk_buff even though the allocator has already reclaimed the object. That is the use-after-free.
The current NVD record describes the same progression: two leading consumed OOB SKBs, a real OOB SKB, a normal receive that reads and frees the third buffer, and a later OOB receive that accesses the freed object. It also identifies the SO_PEEK_OFF logic as part of the path that encounters a zero-length SKB where nonzero progress was expected. (NVD)
Why the Bug Is More Than a Kernel Crash
A use-after-free has at least two possible outcomes.
The simplest outcome is a crash. The stale pointer refers to memory that is invalid, unmapped, poisoned by a debugging allocator, or inconsistent with the operation being performed. A KASAN kernel detects this and emits a report before the corruption becomes silent.
The more serious outcome occurs when another allocation reuses the freed memory. The stale pointer then refers to a different live object or attacker-influenced content. Code that believes it is operating on an SKB may read or modify fields belonging to the replacement allocation.
Exploitability depends on factors such as:
- The allocator cache from which the object came
- The size and alignment of the freed allocation
- Which objects can occupy the same memory
- Whether the attacker can control their contents
- Which fields the stale path reads or changes
- Available timing and synchronization mechanisms
- Kernel hardening
- CPU architecture
- Distribution-specific code and configuration
The official KASAN trace places the freed object in skbuff_head_cache and shows that the invalid read occurs inside the AF_UNIX stream receive path. The upstream fix changed only net/unix/af_unix.c, with 11 additions and two deletions, but the small diff should not be confused with low impact. The change restores an object-lifetime invariant at a kernel privilege boundary. (GitHub)
Project Zero went beyond crashing a sanitizer build. The research analyzed how the dangling SKB reference could provide constrained read and modification behavior, how freed memory could be reallocated in useful forms, and how other permitted kernel interfaces could be combined with those primitives. The final demonstration obtained control over page-table contents, mapped kernel memory writable into userspace, and altered the UTS information printed by uname. (Project Zero)
That result establishes practical exploit potential under a defined environment. It does not establish that every vulnerable-looking kernel is equally exploitable or that the exact research chain will work across distributions, architectures, allocator settings, or kernel builds.
How the Fix Restores the Queue Invariant

The upstream patch is titled af_unix: Don't leave consecutive consumed OOB skbs. Its strategy is more precise than adding defensive loops to every code path that might encounter the problematic queue state.
When an OOB SKB is received in unix_stream_recv_urg(), the patched code examines the preceding buffer. If that predecessor is a consumed OOB SKB with no remaining data, the code unlinks and frees it. The receive queue is therefore prevented from accumulating consecutive consumed OOB entries. (OpenWall Lists)
Conceptually, the patch converts this permitted state:
consumed OOB, length 0
consumed OOB, length 0
active OOB, length 1
into a state in which the old consumed marker is removed before another one can remain behind:
consumed OOB, length 0
active OOB, length 1
The distinction matters because earlier logic had already been adjusted to handle one leading zero-length OOB marker. It did not correctly preserve all required pointer checks after traversing two consecutive markers.
The patch therefore repairs the producer of the invalid queue topology rather than making each consumer understand an increasingly complex set of exceptional arrangements. This is a common defensive design pattern: when several downstream operations depend on a structural invariant, enforce the invariant at the point where the structure is modified.
The Linux kernel CVE announcement recommends updating to the latest stable kernel rather than cherry-picking an isolated commit. It explicitly notes that individual changes are not tested alone and that stable kernel changes are validated as a larger release. (OpenWall Lists)
That advice is especially relevant for CVE-2025-38236 because the vulnerable behavior depends on the interaction of multiple changes over time. A commit may apply cleanly while still being incomplete for a vendor tree with different backports or surrounding logic.
Affected Versions and the Version-Mapping Problem
Version analysis for CVE-2025-38236 requires more care than copying one range from a scanner.
The original AF_UNIX OOB feature landed in Linux 5.15 through commit 314001f0bf927015e459c9d387d62a231fe93af3. The Linux CVE announcement uses that commit as the introduction point and originally listed fixes in the 6.1, 6.6, 6.12, 6.15, and 6.16 branches. (OpenWall Lists)
The NVD record was later updated by kernel.org on June 17, 2026. Its current semantic-version mapping identifies the following versions as fixed within their branches:
| Stable branch | Current fixed version shown in NVD |
|---|---|
| 5.15 | 5.15.194 |
| 6.1 | 6.1.143 |
| 6.6 | 6.6.96 |
| 6.12 | 6.12.36 |
| 6.15 | 6.15.5 |
| Mainline | 6.16 |
The same update maps the affected program file to net/unix/af_unix.c and includes branch-specific fixing commits. (NVD)
Project Zero’s source-history analysis introduces an important qualification. Jann Horn explains that the specific manage_oob() change that created the two related UAF conditions was introduced in mid-2024 and was backported to Linux 6.9.8, but not to older LTS branches at that time. Project Zero therefore describes CVE-2025-38236 as affecting Linux 6.9 and later in the tested code lineage. (Project Zero)
These statements appear inconsistent if version numbers are treated as the entire vulnerability definition:
- Kernel CVE metadata traces the issue to the original Linux 5.15 OOB feature.
- Project Zero traces the concrete vulnerable behavior to a later
manage_oob()fix that entered the 6.9 line. - Current NVD metadata includes fixed releases for older stable branches, including 5.15.194.
The safest interpretation is not to declare one range universally correct for every distribution. Linux vulnerability ancestry, stable backports, and vendor package status are different layers of evidence.
The original feature created the state model. A later change modified the way zero-length OOB buffers were traversed. Stable maintainers may backport fixes, prerequisite changes, or defensive cleanups to branches whose source no longer matches the historical mainline sequence. A vendor may also carry its own patch set.
Use the following hierarchy when making a production decision:
- Check the advisory for the exact operating-system vendor and release.
- Identify the installed kernel package.
- Identify the kernel that is currently running.
- Check whether the vendor package contains the relevant fix or equivalent backport.
- Review kernel configuration and feature reachability.
- Use upstream version mappings as supporting evidence, not as the sole verdict.
A generic scanner that sees 5.15.x may report an exposure based on upstream metadata even when the vendor has backported the fix under an older-looking package version. The reverse can also happen: a new package may be installed on disk while the system continues running an older vulnerable kernel because it has not rebooted.
Vendor Package Status Is More Useful Than a Raw Kernel Number
Linux vendors make their own applicability and scoring decisions because they know which kernel branches, configurations, and backports they ship.
Amazon Linux illustrates the point clearly. Its advisory marks Amazon Linux 1, the Amazon Linux 2 core kernel, and Amazon Linux 2’s 5.4 and 5.10 kernel packages as not affected. It identifies fixes for the Amazon Linux 2 5.15 package and for Amazon Linux 2023 packages, including the 6.12 package. (Explore AWS)
Oracle rates the issue Important and publishes a CVSS 3.1 score of 7.8. Its advisory lists Unbreakable Enterprise Kernel errata released across Oracle Linux versions 8, 9, and 10 in September, October, and November 2025. (Oracle Linux)
SUSE also published multiple security updates beginning in August 2025, with package applicability varying across its product families and kernel flavors. (SUSE)
These vendor records lead to an operational rule:
upstream version range
+
distribution source package
+
vendor backport status
+
running kernel
+
system configuration
=
defensible exposure decision
Do not reduce that equation to the first line.
Useful inventory commands
The following commands do not trigger the vulnerable path. They collect information needed to compare a system with its vendor advisory.
# Running kernel release
uname -r
# Full build string
cat /proc/version
# Kernel command line
cat /proc/cmdline
# Distribution identity
cat /etc/os-release
On Debian and Ubuntu systems:
dpkg-query -W \
-f='${Package}\t${Version}\t${Architecture}\n' \
'linux-image*' 2>/dev/null | sort
On RPM-based systems:
rpm -qa --qf '%{NAME}\t%{VERSION}-%{RELEASE}.%{ARCH}\n' \
| grep -E '^kernel(|-core|-uek|-default|-longterm)[[:space:]]' \
| sort
On systems that expose the running configuration through /proc:
zgrep -E '^CONFIG_(UNIX|AF_UNIX_OOB)=' /proc/config.gz 2>/dev/null
Otherwise, try the boot configuration:
config="/boot/config-$(uname -r)"
if [ -r "$config" ]; then
grep -E '^CONFIG_(UNIX|AF_UNIX_OOB)=' "$config"
else
printf 'No readable kernel config found at %s\n' "$config" >&2
fi
A result such as the following confirms that AF_UNIX OOB support was built:
CONFIG_UNIX=y
CONFIG_AF_UNIX_OOB=y
It does not, by itself, prove that the running package contains the vulnerable code sequence. Conversely, an absent config file does not prove the feature is disabled.
Why CVSS Scores Differ
NVD and Oracle use the following CVSS 3.1 vector for CVE-2025-38236:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
That produces a base score of 7.8. The vector treats the attack as local, requiring low privileges and no user interaction, with potentially high impact to confidentiality, integrity, and availability. (Oracle Linux)
Amazon Linux assigns a score of 7.0 and changes Attack Complexity from Low to High:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Its advisory displays both its own score and NVD’s 7.8 assessment. (Explore AWS)
The difference reflects a judgment about exploit conditions, not a disagreement that the memory-safety bug exists.
Triggering the KASAN failure is deterministic and requires only a short socket sequence. Developing a reliable privilege-escalation exploit is more difficult. It may require controlled heap reuse, information about allocator state, concurrent activity, architecture-specific techniques, and ways to convert limited corruption into broader memory access.
A defender should therefore separate three questions:
| Question | Evidence |
|---|---|
| Can the bug be triggered? | Official reproducer and KASAN trace |
| Can the memory corruption be developed into kernel compromise? | Project Zero exploitation demonstration |
| Is exploitation equally reliable on this exact fleet? | Requires vendor, architecture, configuration, and workload analysis |
A base score is a useful sorting signal. It is not a substitute for those questions.
The Chrome Renderer Demonstration
The most consequential public analysis of CVE-2025-38236 is Project Zero’s work on the Chrome Linux desktop renderer sandbox.
Chrome did not need AF_UNIX urgent data for ordinary renderer operation. The renderer sandbox nevertheless permitted stream-oriented Unix sockets and did not sufficiently restrict the flags supplied to the relevant send and receive operations. As a result, renderer code could reach MSG_OOB. Chrome later blocked sending OOB messages from renderer processes in response to the issue. (Project Zero)
The starting condition was native code execution inside a renderer. That is already a serious browser compromise, but a sandbox is intended to prevent renderer code from becoming unrestricted host code. CVE-2025-38236 provided a path from that contained process into the Linux kernel.
The research used many interfaces that remained available in the sandbox, including Unix stream and datagram sockets, pipes, eventfd, madvise, mprotect, munmap, mincore, clone, poll, and sendmsg. Some were required for ordinary renderer behavior. Others represented less obvious attack surface that could support exploitation once a kernel memory-corruption primitive existed. (Project Zero)
The chain is technically significant for two reasons.
First, it shows that a seemingly narrow one-byte urgent-data feature can become the entry point for a complete kernel attack when combined with memory-allocation and synchronization primitives.
Second, it shows the limitations of sandbox rules that reason only about syscall names. A rule may allow sendmsg() because an application needs ordinary IPC. That decision may unintentionally allow ancillary control-message behavior, uncommon address families, unusual flags, or protocol-specific subfeatures.
The lesson is not that every syscall argument should be blocked indiscriminately. It is that sandbox design should document why each mode is needed.
For each permitted socket operation, a reviewer should ask:
- Which socket families can the process create?
- Which socket types are necessary?
- Which
sendandrecvflags are required? - Can the process pass file descriptors?
- Can it create abstract Unix sockets?
- Can it use urgent data?
- Can it request uncommon ancillary messages?
- Can it select kernel features unrelated to its intended workload?
Project Zero’s conclusion extends beyond AF_UNIX. Core interfaces often hide rarely used features behind flags. Such features may receive less production use and less review while remaining reachable from highly exposed sandboxed code. (Project Zero)
Public Exploit Research Does Not Automatically Mean Active Exploitation
CVE-2025-38236 has public trigger information and public exploit research. Those facts justify prompt patching in environments that execute untrusted code.
They do not justify claiming that the vulnerability has been used in widespread attacks without separate evidence.
The Project Zero work is a controlled research demonstration. It proves that, on the tested Debian Trixie x86-64 environment, the bug could be developed from Chrome renderer code execution into kernel memory manipulation. It also makes substantial technical information publicly available. (Project Zero)
That changes attacker economics. A capable researcher no longer needs to start from an unexplained crash. The relevant data structures, allocator challenges, and possible exploitation directions have been analyzed publicly.
However, exploit development still depends on the target. Kernel configuration, allocator behavior, compiler choices, Control-Flow Integrity, architecture, hardening, distribution patches, and workload noise may affect reliability.
Security communication should use precise language:
- “A public kernel exploitation demonstration exists” is supported.
- “The vulnerability can potentially enable local privilege escalation or sandbox escape” is supported.
- “Every affected kernel can be rooted with the same script” is not supported.
- “The vulnerability is being actively exploited at scale” requires evidence beyond the public research.
This distinction matters for credibility and prioritization. Exaggerating exploitation status can be as harmful as understating technical impact.
Risk Prioritization by Workload
Patch priority should reflect both kernel exposure and the likelihood that hostile code can reach the local socket API.
| Workload | Untrusted local code | Shared kernel value | Suggested priority |
|---|---|---|---|
| Shared CI runner accepting external pull requests | Frequent | High | Urgent |
| Multi-tenant container host | Frequent | Very high | Urgent |
| Browser-heavy developer workstation | Plausible | High user data value | High |
| Notebook or ML execution platform | Frequent | High | Urgent |
| Plugin or extension execution service | Plausible | Medium to high | High |
| Single-purpose server with no shell users | Limited | High asset value | Medium to high |
| Offline appliance with signed workloads only | Unlikely | Product-specific | Vendor-led assessment |
| Disposable single-user test VM | Controlled | Low | Planned update |
Shared CI runners
CI workers exist to execute code. Even when builds run under an unprivileged user or inside a container, they often share the host kernel and may have access to source code, signing material, deployment tokens, package credentials, or cloud identities.
A kernel exploit can turn a malicious test case into control over the worker host. If workers are reused, the attacker may affect later jobs. If several tenants share a node, a host compromise can cross project boundaries.
Container hosts
A container is not a separate kernel. Containerized processes invoke the host’s system calls unless a policy blocks them. Namespaces and capabilities restrict what a process can do through valid kernel behavior; they do not make a memory-safety flaw safe.
CVE-2025-38236 does not require Netfilter configuration or a network namespace in the way some other local kernel exploits do. The essential primitive is access to AF_UNIX stream sockets and the relevant OOB operations.
A container policy that blocks MSG_OOB may reduce exposure, but that conclusion must be tested against the exact runtime, architecture, and syscall wrappers. A generic statement that “seccomp is enabled” is insufficient.
Browser workstations
The Project Zero case directly establishes browser-sandbox relevance. A browser remote-code-execution vulnerability and CVE-2025-38236 would be separate vulnerabilities, but together they could form a renderer-to-kernel chain.
Users do not need to use Unix urgent data intentionally. The question is whether compromised renderer code can invoke it.
Notebook and code-execution platforms
Jupyter-style services, online IDEs, data-science environments, and model-training platforms frequently permit native extensions, subprocesses, package installation, or user-supplied binaries. Their main security boundary may be a container or restricted Unix account.
Because these systems are designed to execute code, local privilege escalation deserves higher weight than on a fixed-purpose appliance.
Embedded and OT products
Embedded products may include Linux while exposing no ordinary shell to end users. Their practical attack path can be narrower. They may also have long maintenance windows, specialized kernels, and difficult reboot requirements.
The correct response is to follow the product vendor’s advisory rather than replacing firmware based solely on an upstream kernel number. NVD has added product-specific records for some Siemens SIMATIC S7-1500 MFP variants, illustrating how a Linux kernel CVE can propagate into industrial products with their own remediation process. (NVD)
Safe PoC: Model the Dangling Pointer Without Calling the Kernel

Running the official socket sequence against a vulnerable kernel can produce a kernel memory-safety failure. A failed attempt can panic a host or leave it in an uncertain state. It should not be run on a production machine.
The following Python program is a toy state-machine demonstration. It does not create sockets, use MSG_OOB, invoke the vulnerable kernel functions, access kernel memory, or attempt privilege escalation. Its only purpose is to make the object-lifetime failure understandable.
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
@dataclass
class SimulatedSKB:
name: str
length: int = 1
consumed: int = 0
freed: bool = False
@property
def remaining(self) -> int:
if self.freed:
raise RuntimeError(f"{self.name} was accessed after being freed")
return self.length - self.consumed
class VulnerableUnixSocketModel:
"""
Educational model only.
This class does not use real sockets or kernel APIs.
It models the queue and pointer relationships relevant to
CVE-2025-38236.
"""
def __init__(self) -> None:
self.queue: list[SimulatedSKB] = []
self.oob_skb: Optional[SimulatedSKB] = None
self.counter = 0
def send_oob(self) -> None:
self.counter += 1
skb = SimulatedSKB(name=f"skb-{self.counter}")
self.queue.append(skb)
self.oob_skb = skb
print(f"send_oob: {skb.name} is now the active OOB skb")
def recv_oob(self) -> None:
if self.oob_skb is None:
raise RuntimeError("No active OOB skb")
skb = self.oob_skb
if skb.freed:
raise RuntimeError(
f"USE-AFTER-FREE: oob_skb still points to {skb.name}"
)
skb.consumed = skb.length
self.oob_skb = None
print(
f"recv_oob: consumed {skb.name}; "
f"remaining={skb.remaining}; it stays in the queue"
)
def vulnerable_normal_recv(self) -> None:
"""
Simplified vulnerable behavior.
It skips two consumed queue entries, frees the next live entry,
but forgets to clear oob_skb when that entry is the active OOB skb.
"""
skipped = 0
while self.queue and self.queue[0].remaining == 0 and skipped < 2:
old = self.queue.pop(0)
old.freed = True
skipped += 1
print(f"normal_recv: removed consumed marker {old.name}")
if not self.queue:
raise RuntimeError("No readable skb")
skb = self.queue.pop(0)
print(f"normal_recv: read {skb.name}")
skb.freed = True
# The modeled bug:
# self.oob_skb is not cleared when it still points to skb.
print(
f"normal_recv: freed {skb.name}, "
"but the OOB pointer was not synchronized"
)
def show_pointer(self) -> None:
if self.oob_skb is None:
print("oob_skb: NULL")
return
state = "freed" if self.oob_skb.freed else "live"
print(f"oob_skb: {self.oob_skb.name}, state={state}")
def main() -> None:
sock = VulnerableUnixSocketModel()
sock.send_oob()
sock.recv_oob()
sock.send_oob()
sock.recv_oob()
sock.send_oob()
print("\nBefore normal receive:")
sock.show_pointer()
sock.vulnerable_normal_recv()
print("\nAfter normal receive:")
sock.show_pointer()
print("\nFinal OOB receive:")
try:
sock.recv_oob()
except RuntimeError as exc:
print(exc)
if __name__ == "__main__":
main()
Expected output resembles:
send_oob: skb-1 is now the active OOB skb
recv_oob: consumed skb-1; remaining=0; it stays in the queue
send_oob: skb-2 is now the active OOB skb
recv_oob: consumed skb-2; remaining=0; it stays in the queue
send_oob: skb-3 is now the active OOB skb
Before normal receive:
oob_skb: skb-3, state=live
normal_recv: removed consumed marker skb-1
normal_recv: removed consumed marker skb-2
normal_recv: read skb-3
normal_recv: freed skb-3, but the OOB pointer was not synchronized
After normal receive:
oob_skb: skb-3, state=freed
Final OOB receive:
USE-AFTER-FREE: oob_skb still points to skb-3
The model deliberately simplifies locking, reference counting, queue traversal, SO_PEEK_OFF, SKB allocation, and the real receive actor. It preserves the security-relevant relationship:
object freed
+
direct pointer not cleared
+
later pointer use
=
use-after-free
Modeling the patched invariant
A simplified preventive operation can remove the preceding consumed marker whenever another OOB receive would otherwise create consecutive zero-length entries:
def patched_recv_oob(self) -> None:
if self.oob_skb is None:
raise RuntimeError("No active OOB skb")
skb = self.oob_skb
if skb.freed:
raise RuntimeError("Unexpected stale OOB pointer")
skb.consumed = skb.length
self.oob_skb = None
index = self.queue.index(skb)
if index > 0:
previous = self.queue[index - 1]
if previous.remaining == 0:
self.queue.pop(index - 1)
previous.freed = True
print(f"patched_recv_oob: removed {previous.name}")
print(f"patched_recv_oob: consumed {skb.name}")
This is not a line-for-line implementation of the kernel patch. It demonstrates the patch’s structural objective: prevent consecutive consumed OOB markers from remaining in the receive queue.
Authorized Lab Validation
A real validation should use a disposable virtual machine, not a production host.
A suitable lab has:
- A snapshot that can be restored
- No production credentials
- No shared storage containing sensitive data
- A serial console or out-of-band log channel
- A KASAN-enabled test kernel when practical
- Resource limits that contain a crash
- A known vulnerable build and a known fixed build
- Identical test conditions across both builds
The official report demonstrates a KASAN slab-use-after-free. KASAN is valuable because it detects invalid object use close to the point of access rather than waiting for secondary corruption. The upstream report identifies the failure in unix_stream_read_actor and shows the allocation and free histories for the affected SKB. (GitHub)
Pre-test evidence collection
Record the following before testing:
set -eu
printf '%s\n' '=== Distribution ==='
cat /etc/os-release
printf '%s\n' '=== Running kernel ==='
uname -a
cat /proc/version
printf '%s\n' '=== Boot command line ==='
cat /proc/cmdline
printf '%s\n' '=== Relevant configuration ==='
if [ -r /proc/config.gz ]; then
zgrep -E '^CONFIG_(UNIX|AF_UNIX_OOB|KASAN)=' /proc/config.gz || true
elif [ -r "/boot/config-$(uname -r)" ]; then
grep -E '^CONFIG_(UNIX|AF_UNIX_OOB|KASAN)=' \
"/boot/config-$(uname -r)" || true
else
printf '%s\n' 'Kernel configuration is not exposed'
fi
Save a cryptographic hash of the evidence:
sha256sum lab-kernel-evidence.txt
Compare installed and running kernels
On Debian-family systems:
running="$(uname -r)"
printf 'Running kernel: %s\n' "$running"
dpkg-query -W \
-f='${Package}\t${Version}\n' \
'linux-image*' 2>/dev/null \
| sort
On RPM-family systems:
printf 'Running kernel: %s\n' "$(uname -r)"
rpm -qa --qf '%{NAME}\t%{VERSION}-%{RELEASE}.%{ARCH}\n' \
| grep '^kernel' \
| sort
A fixed package installed on disk does not protect the host until the system is running the updated kernel. Live-patching technologies may change that conclusion for a particular vendor, but such coverage must be confirmed from the live-patch provider rather than assumed.
Lab result interpretation
A strong validation result has two sides:
| Test | Expected observation |
|---|---|
| Vulnerable lab build | Reproducer reaches the documented KASAN use-after-free |
| Fixed lab build | Same sequence does not produce the use-after-free |
| Configuration comparison | AF_UNIX OOB and sanitizer settings are documented |
| Code comparison | Vendor or upstream fix is present |
| Operational check | Updated system is actually booted into the fixed build |
A single non-crashing run is weak evidence. The test may have used a kernel without the expected code combination, the feature may have been disabled, the reproducer may not have reached the intended path, or the build may lack KASAN.
The reverse is also important: a KASAN crash proves a memory-safety defect in the lab build, but it does not prove reliable privilege escalation against every production machine with a similar version string.
Detecting Suspicious MSG_OOB Activity
CVE-2025-38236 does not produce a universal network signature. The operations are local Unix socket calls, and successful exploitation may avoid a visible crash.
Useful telemetry falls into three categories:
- Kernel memory-safety and crash logs
- Unusual use of
MSG_OOB - Suspicious behavior after a possible privilege transition
Search kernel logs
journalctl -k --since '24 hours ago' \
| grep -Ei \
'KASAN|use-after-free|unix_stream_read_actor|unix_stream_read_generic|af_unix|skbuff_head_cache'
For systems without persistent journal storage:
dmesg --color=never \
| grep -Ei \
'KASAN|use-after-free|unix_stream_read_actor|unix_stream_read_generic|af_unix|skbuff_head_cache'
Potentially relevant strings include:
BUG: KASAN: slab-use-after-free
unix_stream_read_actor
unix_stream_read_generic
skbuff_head_cache
These strings are evidence of a kernel memory-safety event, not a unique signature of malicious exploitation. Kernel testing, unrelated AF_UNIX bugs, or another SKB corruption issue could produce overlapping output.
Observe OOB calls with bpftrace
The following defensive tracing example reports processes that pass the MSG_OOB bit, whose value is 0x1, to common send and receive syscalls.
Before using it, confirm the tracepoint argument names on the target kernel:
cat /sys/kernel/tracing/events/syscalls/sys_enter_sendto/format
cat /sys/kernel/tracing/events/syscalls/sys_enter_recvfrom/format
cat /sys/kernel/tracing/events/syscalls/sys_enter_sendmsg/format
cat /sys/kernel/tracing/events/syscalls/sys_enter_recvmsg/format
Example script:
#!/usr/bin/env bpftrace
BEGIN
{
printf("Tracing socket operations using MSG_OOB. Press Ctrl-C to stop.\n");
}
tracepoint:syscalls:sys_enter_sendto
/(args->flags & 0x1) != 0/
{
printf(
"sendto MSG_OOB pid=%d uid=%d comm=%s fd=%d flags=0x%x\n",
pid, uid, comm, args->fd, args->flags
);
}
tracepoint:syscalls:sys_enter_recvfrom
/(args->flags & 0x1) != 0/
{
printf(
"recvfrom MSG_OOB pid=%d uid=%d comm=%s fd=%d flags=0x%x\n",
pid, uid, comm, args->fd, args->flags
);
}
tracepoint:syscalls:sys_enter_sendmsg
/(args->flags & 0x1) != 0/
{
printf(
"sendmsg MSG_OOB pid=%d uid=%d comm=%s fd=%d flags=0x%x\n",
pid, uid, comm, args->fd, args->flags
);
}
tracepoint:syscalls:sys_enter_recvmsg
/(args->flags & 0x1) != 0/
{
printf(
"recvmsg MSG_OOB pid=%d uid=%d comm=%s fd=%d flags=0x%x\n",
pid, uid, comm, args->fd, args->flags
);
}
Run it only under an authorized administrative account:
sudo bpftrace ./trace-msg-oob.bt
A detection system should enrich the event with:
- Executable path
- Container identity
- Cgroup
- Parent process
- User
- Sandbox profile
- Command line
- Code-signing or package origin
- Whether the process is expected to use urgent socket data
MSG_OOB is legitimate for some software. An alert should not automatically terminate every process that uses it. It is much more suspicious when observed from a browser renderer, untrusted build job, document converter, or application that has no declared need for urgent Unix socket data.
Tracing also has limitations. A compromised kernel may be able to interfere with kernel-based telemetry. Different architectures and compatibility layers may expose different syscall paths. Some 32-bit environments use multiplexed socket interfaces. Production eBPF deployment must therefore be validated against the exact platform.
Restricting MSG_OOB with Seccomp
Patching the kernel is the primary remediation. A seccomp rule can serve as a compensating control for processes that do not need OOB socket operations.
The key improvement over a syscall-name-only policy is argument filtering. The policy can allow ordinary sendto, recvfrom, sendmsg, and recvmsg operations while rejecting calls whose flags contain MSG_OOB.
The following C fragment illustrates the defensive approach with libseccomp:
#include <errno.h>
#include <linux/seccomp.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
static void check_rule(int rc, const char *name)
{
if (rc < 0) {
fprintf(stderr, "Failed to add %s rule: %d\n", name, rc);
exit(EXIT_FAILURE);
}
}
int install_no_msg_oob_filter(void)
{
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
fprintf(stderr, "seccomp_init failed\n");
return -1;
}
/*
* sendto and recvfrom:
* flags is syscall argument 3 on common 64-bit Linux ABIs.
*/
check_rule(
seccomp_rule_add(
ctx,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(sendto),
1,
SCMP_A3(SCMP_CMP_MASKED_EQ, MSG_OOB, MSG_OOB)
),
"sendto MSG_OOB"
);
check_rule(
seccomp_rule_add(
ctx,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(recvfrom),
1,
SCMP_A3(SCMP_CMP_MASKED_EQ, MSG_OOB, MSG_OOB)
),
"recvfrom MSG_OOB"
);
/*
* sendmsg and recvmsg:
* flags is syscall argument 2 on common 64-bit Linux ABIs.
*/
check_rule(
seccomp_rule_add(
ctx,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(sendmsg),
1,
SCMP_A2(SCMP_CMP_MASKED_EQ, MSG_OOB, MSG_OOB)
),
"sendmsg MSG_OOB"
);
check_rule(
seccomp_rule_add(
ctx,
SCMP_ACT_ERRNO(EPERM),
SCMP_SYS(recvmsg),
1,
SCMP_A2(SCMP_CMP_MASKED_EQ, MSG_OOB, MSG_OOB)
),
"recvmsg MSG_OOB"
);
if (seccomp_load(ctx) < 0) {
fprintf(stderr, "seccomp_load failed\n");
seccomp_release(ctx);
return -1;
}
seccomp_release(ctx);
return 0;
}
This example is not a universal drop-in sandbox policy.
A production implementation must account for:
- The target CPU architecture
- 32-bit compatibility syscalls
- The process’s libc wrappers
- Existing seccomp policy
- Whether
send,recv,sendmmsg, orrecvmmsgrequire additional treatment - Whether a supervisor or broker performs socket operations on behalf of the process
- Legitimate application use of
MSG_OOB
Test the filter with the real application. A security control that silently breaks IPC may be disabled during an incident, leaving the original exposure unchanged.
Chrome’s response demonstrates the value of this narrow approach. It did not need to remove all Unix sockets from renderer processes. It blocked the unnecessary OOB mode that made the vulnerable kernel path reachable. (Project Zero)
Disabling AF_UNIX OOB in Custom Kernels
Project Zero notes that AF_UNIX OOB support was enabled by default with Unix socket support and only became a separately selectable configuration option after a December 2024 change introduced a prompt for CONFIG_AF_UNIX_OOB. (Project Zero)
Organizations that build their own kernels can consider:
CONFIG_AF_UNIX_OOB=n
This is appropriate only after confirming that deployed applications do not require urgent data over Unix stream sockets.
The advantages are straightforward:
- The vulnerable feature becomes unavailable
- Future bugs in the same optional code are unreachable
- Sandbox policies no longer need to distinguish OOB use for that kernel
The limitations are equally important:
- It requires building and distributing a new kernel
- The system must reboot into that kernel
- Vendor support may be affected
- Applications with a genuine dependency can break
- It does not replace other kernel security updates
- It does not prove that a previously running vulnerable system was not compromised
Most enterprises should prefer their vendor’s fixed kernel package. Configuration removal is most suitable for appliances, hardened sandbox hosts, and purpose-built systems with a controlled software inventory.
Remediation Workflow
A defensible response to CVE-2025-38236 has four stages: establish applicability, patch, activate the fix, and verify.
Establish applicability
Record:
- Distribution and release
- Kernel package name
- Installed package version
- Running kernel version
- Architecture
- Vendor advisory status
CONFIG_AF_UNIX_OOB- Whether untrusted local code runs
- Whether sandboxed processes can pass
MSG_OOB - Reboot and maintenance constraints
Install the supported update
Use the distribution’s normal package and advisory channel. Avoid replacing a vendor kernel with an arbitrary upstream build unless that is already part of the organization’s supported operating model.
The upstream kernel team recommends taking a complete stable release rather than cherry-picking a single fix because changes are tested together and some security outcomes depend on multiple related patches. (OpenWall Lists)
Reboot or activate the vendor-supported fix
After installation:
uname -r
cat /proc/version
Compare the output with the vendor advisory and package record.
A useful deployment check is to identify machines where the newest installed kernel differs from the running kernel. The exact implementation depends on the distribution, but the desired assertion is simple:
fixed package installed
AND
fixed kernel running
Retest controls
After reboot:
- Confirm the expected kernel package.
- Confirm the expected running release.
- Confirm the relevant configuration.
- Confirm that sandbox rules reject
MSG_OOBwhere intended. - In a separate disposable lab, repeat the safe differential test between vulnerable and fixed builds.
- Store the commands, output, timestamps, package identifiers, and change ticket.
Preserve rollback without preserving exposure
Keep a known-good rollback path, but do not leave a vulnerable kernel as the default boot entry indefinitely. Restrict access to bootloader changes. In cloud environments, update the base image and autoscaling template so replacement instances do not reintroduce the old kernel.
Detection Is Not a Substitute for Patching
It may be tempting to monitor MSG_OOB and defer a reboot. That can be reasonable for a tightly bounded maintenance window, but it is not equivalent to removing the bug.
Detection can fail because:
- Legitimate and malicious socket calls may look similar
- A successful exploit may avoid a KASAN-style crash
- Production kernels usually do not run KASAN
- Short-lived processes may execute before telemetry attaches
- Container identity may be missing from basic audit logs
- Kernel compromise can undermine kernel-resident sensors
- An attacker may use a different local escalation vulnerability
Compensating controls should reduce exposure during patch rollout:
restrict untrusted code
+
block unnecessary MSG_OOB
+
increase kernel telemetry
+
shorten patch window
They should not become a permanent reason to retain a known-vulnerable kernel.
Containers and Kubernetes
CVE-2025-38236 should be assessed at the node level, not by scanning only container filesystems.
The kernel version visible from inside a container is normally the host kernel:
uname -r
Installing a kernel package inside an ordinary container does not update the host. Rebuilding an application image also does not remediate the node.
For Kubernetes, the relevant asset is the worker-node image and the running node kernel. A practical rollout may require:
- Build or select a fixed node image.
- Create replacement capacity.
- Cordon a vulnerable node.
- Drain workloads according to disruption policies.
- Replace or reboot the node.
- Verify the running kernel.
- Remove obsolete node images from autoscaling configuration.
- Confirm that new nodes launch with the fixed build.
Prioritize nodes that host:
- Untrusted customer containers
- Public CI jobs
- Code-execution services
- Browser automation
- Document conversion
- Package building
- Security analysis
- Developer workspaces
A container seccomp profile that blocks OOB flags can reduce reachability, but many runtime policies are expressed as allowed syscall names without detailed argument constraints. Review the compiled policy rather than relying on the profile’s name.
User namespaces are not the defining prerequisite for this AF_UNIX bug. Disabling them may reduce exposure to other local kernel attack surfaces, but it does not automatically prevent a process from creating a Unix stream socket pair and using permitted OOB operations.
Incident Response After Suspected Exploitation
A KASAN report, kernel oops, or suspicious OOB sequence does not prove that the attacker obtained root. It warrants investigation because the affected code runs in the kernel and public exploit research demonstrates a path to kernel memory control.
Begin by preserving volatile evidence where operationally safe:
date --iso-8601=seconds
uname -a
cat /proc/version
cat /proc/cmdline
cat /etc/os-release
journalctl -k --no-pager
Record the running container inventory, process tree, network state, mounted filesystems, loaded security modules, and cloud instance identity.
Do not assume that user-space commands are trustworthy after credible kernel compromise. A kernel-level attacker may alter process listings, intercept file reads, manipulate audit events, change credentials, or hide persistence.
Investigation should cover the initial-access stage as well as the escalation stage. CVE-2025-38236 does not explain how attacker code first entered the renderer, container, build worker, or Unix account.
Potential initial-access sources include:
- Browser memory-corruption vulnerability
- Web application command execution
- Malicious dependency
- Compromised build script
- Stolen SSH credential
- Uploaded plugin
- Hostile notebook
- Container escape attempt
- Insider activity
Post-escalation scope should include:
| Asset | Why it matters |
|---|---|
| SSH host and user keys | Root can read or replace them |
| CI secrets | May permit source, artifact, or deployment compromise |
| Cloud credentials | Instance metadata and local tokens may enable lateral movement |
| Container runtime sockets | Can provide control over other workloads |
| Security-agent configuration | Kernel control can disable or deceive monitoring |
| Package-signing keys | Could extend compromise into the supply chain |
| Browser profiles | Tokens, cookies, and stored credentials may be accessible |
| Neighboring tenants | A shared kernel may expose cross-tenant data |
| Boot configuration | Persistence may survive an ordinary process cleanup |
Where kernel compromise is credible, rebuilding from trusted media is usually safer than trying to clean the machine in place. Rotate secrets that were accessible from the host, review downstream activity, and verify that replacement images contain the fixed kernel.
CVE-2025-38236 Compared with CVE-2024-1086
CVE-2024-1086 is another Linux kernel use-after-free associated with local privilege escalation, but it affects Netfilter’s nf_tables subsystem rather than AF_UNIX urgent-data handling.
NVD describes CVE-2024-1086 as a condition in which Netfilter verdict handling can lead to a double free and local privilege escalation. CISA added it to the Known Exploited Vulnerabilities catalog in May 2024, requiring covered federal organizations to apply vendor mitigations or discontinue affected products when mitigations were unavailable. (NVD)
The comparison helps prevent overgeneralization:
| Attribute | CVE-2025-38236 | CVE-2024-1086 |
|---|---|---|
| Kernel subsystem | AF_UNIX stream sockets | Netfilter nf_tables |
| Core weakness | Dangling oob_skb pointer and use-after-free | Use-after-free and double-free behavior in verdict processing |
| Typical starting point | Local low-privileged code | Local low-privileged code |
| Important reachability factor | AF_UNIX OOB and MSG_OOB permitted | Netfilter and required namespace or rule operations reachable |
| Public exploit evidence | Detailed Project Zero sandbox-to-kernel demonstration | Public exploitation material and CISA KEV status |
| Primary mitigation | Vendor kernel update and optional OOB restriction | Vendor kernel update and reduction of exposed Netfilter or namespace paths |
| Detection challenge | Local socket calls may be low-noise | Namespace and Netfilter activity may overlap legitimate container operations |
Both vulnerabilities show why the label “local” is insufficient for triage. Their operational relevance depends on where low-privileged code runs and which kernel subsystem that code can reach.
They also show why one generic “Linux kernel vulnerable” scanner result is not enough. CVE-2024-1086 often requires analysis of namespaces and nf_tables reachability. CVE-2025-38236 requires analysis of AF_UNIX OOB support and socket flag policy. Different code paths demand different validation evidence.
Evidence-Driven Validation at Fleet Scale
Kernel vulnerability management often fails at the handoff between scanning and operations.
A scanner reports a CVE. The platform team installs a package. The security team closes the ticket. Nobody verifies whether the node rebooted, whether the scanner recognized the vendor backport, or whether the vulnerable feature was reachable.
For CVE-2025-38236, a complete evidence record should include:
| Evidence | Example |
|---|---|
| Asset identity | Hostname, instance ID, cluster, node pool |
| Distribution | /etc/os-release |
| Running kernel | uname -r and /proc/version |
| Installed kernel packages | dpkg-query or rpm output |
| Vendor status | Advisory and fixed package identifier |
| Feature configuration | CONFIG_AF_UNIX_OOB |
| Workload trust | Browser, CI, tenant container, appliance |
| Sandbox reachability | Whether MSG_OOB is permitted |
| Remediation | Package transaction and reboot record |
| Verification | Post-reboot kernel output |
| Retest | Fixed-build lab result or control test |
| Exception | Owner, expiry, compensating control, business reason |
For broader Linux kernel triage methodology, the Penligent Linux CVE Hub explains why a CVE entry should begin an applicability investigation rather than act as a binary verdict. An authorized workflow using Penligent can keep asset evidence, validation output, retest results, and reporting context together, but automation should not replace vendor advisory review or the decision to reboot into a fixed kernel.
The most useful automation is not one that launches a public exploit against every host. It is one that reliably answers:
Which systems are running the affected code?
Which systems allow an attacker to reach it?
Which systems have a supported fix?
Which systems installed but did not activate the fix?
Which exceptions are still open?
That workflow scales without turning production infrastructure into a kernel exploitation laboratory.
Common Assessment Mistakes
Treating local as low impact
Local means the attacker needs code execution on the machine. CI, containers, browsers, and notebook services already provide code-execution contexts by design. The correct question is whether that code is trusted with the host kernel.
Trusting only the upstream semantic version
Backports can make an older-looking kernel safe, while unactivated package updates can leave a newer package installed but an older kernel running. Use the vendor advisory and the current running build.
Ignoring the Project Zero version qualification
The upstream CVE mapping traces ancestry to Linux 5.15, while Project Zero identifies a later manage_oob() change entering the 6.9 line as the immediate source of the specific UAF state. Record that nuance instead of presenting a universal range without context. (Project Zero)
Running the trigger on production
A kernel memory-safety PoC can panic the host, corrupt data, or create an unknown security state. Use a disposable lab and a differential vulnerable-versus-fixed test.
Installing the update without rebooting
Kernel packages are not ordinary libraries. The running kernel remains in memory. Verify uname -r after maintenance.
Assuming any seccomp profile blocks the bug
A profile may allow sendto, recvfrom, sendmsg, and recvmsg without filtering MSG_OOB. Review arguments and flags.
Disabling user namespaces and declaring victory
User namespaces are relevant to many local kernel exploits, but they are not the defining trigger for this AF_UNIX path. Test the actual socket operations allowed to the untrusted process.
Equating public exploit research with confirmed campaigns
Public research proves technical feasibility and reduces the work required for future exploit development. Active exploitation requires separate evidence.
Closing a finding because one test did not crash
The environment may lack KASAN, use a different backport combination, or fail to reach the intended path. Negative testing is meaningful only when the preconditions are controlled.
Frequently Asked Questions
What is CVE-2025-38236?
- It is a use-after-free in the Linux kernel’s AF_UNIX stream-socket OOB handling.
- The bug can leave
unix_sock->oob_skbpointing to an SKB that the ordinary receive path has already freed. - A later receive with
MSG_OOBaccesses the stale object. - NVD classifies it as CWE-416 and scores it 7.8 under CVSS 3.1. (NVD)
Can CVE-2025-38236 be exploited remotely?
- It is not a conventional remote, unauthenticated network vulnerability.
- An attacker normally needs local code execution in a process on the Linux host.
- Remote exploitation may still be possible as part of a chain in which another bug first provides code execution inside a browser renderer, service account, container, or sandbox.
- Project Zero demonstrated the escalation stage from Chrome renderer native code execution to kernel memory control. (Project Zero)
Which Linux kernel versions are affected?
- Current NVD metadata lists fixed versions including 5.15.194, 6.1.143, 6.6.96, 6.12.36, 6.15.5, and 6.16. (NVD)
- The upstream CVE mapping traces the issue to the AF_UNIX OOB feature added in Linux 5.15.
- Project Zero reports that the immediate buggy
manage_oob()change entered Linux 6.9.8 and did not initially reach older LTS branches. (Project Zero) - Because of backports and vendor changes, the exact distribution advisory is more reliable than a generic mainline version comparison.
How can I check whether my system is patched?
- Record
uname -r,/proc/version, and/etc/os-release. - Query installed kernel packages with the distribution package manager.
- Compare the running package with the vendor’s CVE advisory.
- Confirm the host rebooted into the fixed kernel.
- Do not assume that installing a new package changed the currently running kernel.
- For custom kernels, also check whether
CONFIG_AF_UNIX_OOBis enabled.
Does disabling user namespaces stop CVE-2025-38236?
- Not by itself.
- The vulnerable path is AF_UNIX stream-socket OOB handling.
- User-namespace restrictions may reduce exposure to other kernel exploit techniques, but they do not automatically block
socketpair,send,recv, orMSG_OOB. - A relevant compensating control must restrict the actual OOB operations or remove the feature from the kernel.
Is a public exploit available, and is the vulnerability exploited in the wild?
- Public trigger information and detailed exploitation research are available.
- Project Zero demonstrated a complete renderer-to-kernel research chain on a defined Debian Trixie x86-64 environment. (Project Zero)
- That demonstration proves serious exploit potential.
- It should not be described as evidence that every vulnerable system is exploitable with identical reliability.
- Public research alone also does not prove widespread malicious exploitation.
What is the safest way to validate the fix?
- Use a disposable virtual machine with no production secrets.
- Take a snapshot before testing.
- Prefer a KASAN-enabled kernel for clear memory-safety reporting.
- Compare one known vulnerable build with one fixed build under identical conditions.
- Collect kernel version, package, configuration, and log evidence.
- Do not run a kernel memory-corruption trigger on a production host.
- Validate production systems through package provenance, running-kernel checks, configuration review, and sandbox-policy testing.
Final Assessment
CVE-2025-38236 is rooted in a rarely used one-byte urgent-data feature, but its security significance is not narrow. The vulnerable code runs in the Linux kernel, the trigger is available to low-privileged local processes when AF_UNIX OOB operations are permitted, and public research has demonstrated a path from a browser renderer sandbox to writable kernel memory.
The most important technical lesson is the broken lifetime invariant. Two consumed OOB SKBs can lead the ordinary receive path to free the live urgent-data SKB without clearing the direct pointer that identifies it. The subsequent urgent receive operates on freed memory.
The most important defensive lesson is that sandbox attack surface includes syscall arguments and ancillary modes, not only syscall names. A process that needs ordinary Unix socket IPC may have no reason to use MSG_OOB.
The correct operational response is to identify the exact vendor kernel, confirm whether the relevant code is present, install the supported complete update, reboot into it, and preserve evidence of the running fixed build. High-risk sandbox, CI, container, notebook, and browser environments should also restrict unnecessary OOB operations. Detection can help during rollout, but it should not become a substitute for removing the vulnerable kernel path.

