Linux kernel CVE-2026-80521: The AF_UNIX garbage collector frees a socket it still keeps on its list
Unix sockets can send each other file descriptors, even to themselves, and that creates cycles of sockets that are kept alive only by each other. For such cycles the Linux kernel has its own garbage collector, which since version 6.10 manages sockets as a graph and caches the groups it finds for the next pass. CVE-2026-80521 sits in exactly that cache. If the collector runs in a narrow window between sending and closing, it frees a socket's entry that still hangs in a cached list, and the next pass touches freed memory. Because every default container may use Unix sockets, this becomes a path from an unprivileged process to root on the host.
TL;DR — 90 seconds
- What: A race in the AF_UNIX garbage collector leaves a freed vertex, the entry of a socket in the collector's graph, in a cached list. The collector's next pass touches it, a use-after-free in the kernel.
- Severity: CVSS 7.8 according to the kernel CNA (
AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H), Amazon Linux and Red Hat land slightly lower at 7.0. A public exploit has been available since 22 September, the flaw is not in the CISA KEV catalog. - Who is affected: Hosts with kernels from 6.10 as well as the long-term branches 6.1 from 6.1.141 and 6.6 from 6.6.93, as long as the fix is missing. The headlines say Ubuntu, but the bug lives in the upstream kernel. Amazon Linux 2 is not affected.
- What to do: Update the kernel and reboot the nodes, or apply and verify the livepatch on Amazon Linux 2023. Upstream fixed it in 6.1.189, 6.6.158, 6.12.111, 6.18.53 and 7.1.10, and in mainline since 7.2, so the new Linux 7.3 from 18 October includes it too.
- What would have protected you: Sandbox runtimes such as gVisor or Kata Containers for untrusted code and dedicated node pools for CI and tenants. Default seccomp, dropped capabilities and user namespaces do not help against this mechanism.
How the collector frees a socket's entry too early
Over a Unix socket a process can send open file descriptors to another process using the SCM_RIGHTS control message. Until the receiver picks up the message with recvmsg(), such a descriptor counts as “in flight” according to unix(7). It then sits in a socket buffer in the receive queue of the target socket and keeps the file it points to alive. This also works with sockets themselves. A socket can be sent to itself, two sockets can reference each other, and if the process then closes all its descriptors, a cycle remains that no process can reach and that reference counting never brings down to zero.
For this case the kernel has its own garbage collector in net/unix/garbage.c. Since Linux 6.10 it works on a graph that Kuniyuki Iwashima introduced with commit 4090fa373f0e (“af_unix: Replace garbage collection algorithm.”). In that graph every socket with descriptors in flight becomes a vertex (struct unix_vertex) and every sent descriptor an edge (struct unix_edge). Using a Tarjan-based algorithm, the collector looks for strongly connected components, called SCCs in the kernel code, meaning groups of sockets that can reach each other. If such a group has no reference from outside any more, it is dead, and the collector purges the buffers that hold it together. So that it does not have to rebuild the whole graph on every pass, it remembers the groups it found in a ring via the scc_entry field. Later passes only walk these rings through unix_walk_scc_fast(), and according to the fix commit a single additional cycle in the graph is enough to force this fast path.
This is exactly where the bug was. When sending, unix_add_edges() publishes the new edges under the unix_gc_lock lock and releases the lock right away. Only afterwards does skb_queue_tail() put the buffer with the descriptors into the queue. The fix commit walks through the sequence with a small example. A socket X refers to itself, and two sockets A and B reference each other. While one thread sends socket B to B itself via X, another closes A and B. If the collector hits exactly the gap between the published edge and the queued buffer, it considers A and B dead. It cannot collect the buffer holding the edge from B to B, though, because that buffer is not in any queue yet, and so B survives for the moment.
On its own this would be harmless, because the next pass would recognise B as dead too. The problem arises during cleanup. When the collector drops buffers, unix_del_edge() removes the corresponding edges, and a vertex without outgoing edges moves to a list for freeing. Until the fix, however, nobody took it out of the scc_entry ring. The ring of the surviving B therefore still points to A's memory, and because X's cycle forces the next pass onto the fast path, unix_walk_scc_fast() follows that pointer into freed memory. This is the classic use-after-free pattern. Whoever fills the freed memory with their own data first decides which values the kernel continues to work with.
Two properties of the design make the bug dangerous. The collector runs in the kernel and is triggered by ordinary socket operations that any process may perform. And the window opens anew with every send, so an attacker can retry as often as needed until the race succeeds once. No special privileges or exotic kernel features are required.
What the fix commit changes
The fix commit 594d90519502 also comes from Kuniyuki Iwashima, and Kyle Zeng reported and reviewed it. It consists of a single line in net/unix/garbage.c. In unix_del_edge(), the branch that moves a vertex without outgoing edges onto the free list now also calls list_del(&vertex->scc_entry). Before, the vertex was only moved to another list there and stayed registered in its group's ring. Now it leaves both structures during cleanup, and no cached ring can point to it any more. The Fixes tag points to 4090fa373f0e, which is exactly the rewrite that introduced the bug.
What I find notable is what the fix does not change. The window between unix_add_edges() and skb_queue_tail() remains, and the commit explicitly calls the temporarily surviving socket unproblematic. The cause was therefore not a missing lock but a cache that lived longer than the objects it pointed to. From this I derive a general rule: every optimisation that keeps the results of one pass for the next creates a second place where an object is registered, and every free path has to know both places.
Two further corrections show that the new collector still needs fine-tuning two years after it arrived. The stable releases of 3 October brought a second use-after-free fix into the long-term branches (“af_unix: Drop all SCM attributes for SOCKMAP.”), whose cause was the assumption that the target of an edge is always still alive. Linux 7.3, released on 18 October, also contains “af_unix: Unify scc_index when finalising SCC in __unix_walk_scc().”, which had already been backported to 6.18.54. According to the commit, a group with several back edges could carry different indices depending on the order of the depth-first search, so the collector did not recognise it as dead and never freed it. This commit names no CVE.
From a container process to root on the host
An attack only needs local code execution on a host with a vulnerable kernel. The required calls are part of every program's daily routine: creating Unix sockets, sending descriptors via SCM_RIGHTS and closing sockets, spread across several threads. The default seccomp profiles of Docker and Kubernetes allow all of this, and no capabilities are needed, which the CVSS rating expresses with PR:L. An ordinary container out of the box is therefore affected as soon as its node's kernel is vulnerable.
Conceptually the attack has three steps. First the attacker triggers the race until the collector frees a vertex that stays in the ring. Then they reallocate the freed memory so that the next pass works with data they control. According to DepthFirst and bex.co, the end result is a root shell on the host that leaves namespaces, cgroups and seccomp behind. This path does not even have to cross a boundary between container and host, because all containers on a node share the same kernel.
How large the damage becomes depends on what lives on the node. Root on a Kubernetes node means access to all containers on that node, to their mounted secrets and ServiceAccount tokens, and to the kubelet's credentials. On EC2 the instance role comes on top, whose credentials the host can retrieve via the metadata service. Nodes that run untrusted code are especially exposed, such as CI runners for merge requests from forks, multi-tenant clusters or services that execute user code. Even without untrusted code, the flaw can become the second link in a chain, for example after code execution in a web application, as TYPO3 extensions allowed several times this year.
In the coverage, the flaw appeared as an Ubuntu problem. That is due to the public exploit, which targets a specific Ubuntu 26.04 kernel, and to the fact that Canonical has not yet shipped a fix for Ubuntu 24.04 and 26.04. Its tracker has been unchanged since 13 October. This reading misleads in both directions, though. The bug sits in upstream code and therefore in every kernel with the new collector, that is mainline from 6.10 and the long-term branches 6.1 from 6.1.141 and 6.6 from 6.6.93, into which the rewrite was backported. Older branches without this backport are not affected. For Amazon Linux 2, ALAS reports all kernel variants as not affected, the core kernel as well as the 5.4, 5.10 and 5.15 extras, and the same applies to Debian 11. An exploit tailored to an Ubuntu build will not run unmodified on other kernels, but the mechanism stays the same.
| CVE | Affected | Fix | Precondition | CVSS |
|---|---|---|---|---|
| CVE-2026-80521 | Linux 6.10 and later, 6.1 from 6.1.141 and 6.6 from 6.6.93 (backport of the new collector) | Upstream 594d90519502; 6.1.189, 6.6.158, 6.12.111, 6.18.53, 7.1.10, mainline from 7.2 (including 7.3) | Local code execution, Unix sockets with SCM_RIGHTS, allowed in default containers | 7.8 (high) per CNA, 7.0 per AWS and Red Hat |
The sources differ slightly on the rating. The kernel CNA assigns 7.8, while Amazon Linux and Red Hat set attack complexity to high because of the race and arrive at 7.0. Red Hat also rates the flaw only Moderate. That does not change the mechanism. DepthFirst used the exploit in Google's kernelCTF against target lts-6.12.95 on 24 July, the slot win was confirmed on 5 August, and on 22 September the team published analysis and exploit code. No in-the-wild exploitation is known so far, the flaw is not in the CISA KEV catalog, and EPSS estimates the probability of exploitation in the next 30 days at 0.17 percent. Since public code exists for a widely used distribution, I consider that figure a lower bound.
Kernel fix, livepatch and the way onto the nodes
Upstream has fixed the flaw since 6 August. The fix has been in mainline since 7.2, in the stable branches since 6.12.111, 6.18.53 and 7.1.10, and in the long-term branches since 6.1.189 and 6.6.158, released on 3 October. Linux 7.3 from 18 October therefore contains it as well. You do not need to move to 7.3 for this flaw, though, because the current stable kernels from 15 October, 6.1.190, 6.6.159, 6.12.113 and 6.18.57, are fixed too. Building a kernel yourself is rarely the right path for production systems. What counts is your distribution's kernel package.
For Amazon Linux 2023, AWS updated all three kernel packages on 30 September: kernel to 6.1.188-233.386.amzn2023 (ALAS2023-2026-3146), kernel6.12 to 6.12.110-135.202.amzn2023 (ALAS2023-2026-3147) and kernel6.18 to 6.18.51-120.163.amzn2023 (ALAS2023-2026-3148). According to the advisory, you apply such an update specifically with, for example, dnf update --advisory ALAS2023-2026-3147 --releasever 2023.12.20260930, and with the respective advisory ID for the other packages. Livepatches for the 6.1, 6.12 and 6.18 series appeared the same day (ALAS2023LIVEPATCH-2026-389 to -423).
Debian shipped the fix for Debian 13 with 6.12.111-1 (DSA-6528-1) and for Debian 12 with 6.1.190-1 (DLA-4841-1). With Wolfi, on the other hand, there is nothing to patch, because according to Chainguard Wolfi does not build its own kernel and uses the one provided by the environment. Updating your Wolfi or Chainguard images therefore changes nothing about this flaw, only the host gets patched.
The livepatch is the fastest route on Amazon Linux 2023, but it has some prerequisites. According to the AWS documentation, Kernel Live Patching is disabled by default. You enable it with the DNF plugin kpatch-dnf and the command sudo dnf kernel-livepatch -y auto, and then start the kpatch.service service from the kpatch-runtime package. AWS provides livepatches for a kernel version for only three months and on a best-effort basis, and the displayed kernel version only changes after a reboot. Whether the patch is really loaded is shown by sudo kpatch list. That is exactly what you should check instead of inferring protection from the installed package.
The most common pitfall is the difference between the installed and the running kernel. A package update only takes effect after a reboot, which on Kubernetes nodes means only once you cordon, drain and reboot the node, or replace it right away with one built from a current image. If you build node images yourself or consume ready-made AMIs, you need an image with the fixed kernel and a rollout across all node groups. The fix itself brings no functional breaking changes, it is only one line. The risk lies rather in the kernel update as a whole, which in a stable jump contains many other changes. That is why even a security kernel goes onto staging nodes first.
Hardening against a bug in the shared kernel
Before getting to individual measures, it helps to classify the problem. The attacker uses no privileges that could be taken away, but system calls that practically every program needs, and the faulty code runs in the kernel that all containers on a node share. Only measures that act at one of three points are therefore effective: untrusted code never gets to see the host kernel, the attack surface of individual workloads is deliberately reduced, or a successful escape only hits a node of little value. On top of that comes the speed at which a fix reaches every node.
gVisor as a sandbox for untrusted code
gVisor puts its own user-space kernel, the Sentry, between the application and the host. According to the project's security model, the Sentry passes no application system call directly to the host but implements the interface itself. It may not create new host sockets without host networking. The Unix sockets of an application inside gVisor therefore never reach the host kernel's collector, and that is exactly how the bex.co analysis explains why gVisor protects here. In Kubernetes you integrate gVisor through a RuntimeClass. In containerd you register a runsc runtime handler with runtime_type = "io.containerd.runsc.v1", create a RuntimeClass with handler: runsc and set runtimeClassName in the pods. Via the RuntimeClass's scheduling.nodeSelector you make sure such pods only land on nodes where gVisor is installed.
The limits are known. gVisor does not implement every system call, and I/O-heavy jobs run slower, so you test each workload first. For CI, gVisor is still often the best lever. The GitLab Runner with the Kubernetes executor offers the runtime_class_name key in [runners.kubernetes], which sets a RuntimeClass for all job pods. Together with node_selector and node_tolerations, merge requests from external forks then run in a sandbox on dedicated nodes.
Kata Containers and microVMs
Kata Containers goes one step further and starts each pod in a lightweight virtual machine with its own kernel, isolated through hardware virtualisation. If the guest kernel contains the same bug, it is just as vulnerable. A successful escape then ends inside the VM rather than on the host, though, and for tenant separation that is precisely the decisive difference. DepthFirst explicitly recommends this class, Firecracker or Kata, for workloads you do not trust. The price is higher resource usage per pod, which you plan for in the RuntimeClass via overhead.podFixed.
Your nodes also have to be able to start virtual machines themselves. On EC2 that was a hurdle for a long time, because the node already runs inside a VM there, which is why AWS recommended bare metal instances such as i3.metal for Kata. Since February 2026, according to the AWS documentation, EC2 supports nested virtualization with KVM as the hypervisor, currently on the M7i, M8i, C7i, C8i, R7i, R8i, X8i, I7i and I7ie families and their variants. You enable it per instance via the CPU option NestedVirtualization=enabled, at no extra cost, and whether an instance type supports it is shown by aws ec2 describe-instance-types with the nested-virtualization feature. For performance- or latency-sensitive workloads AWS still recommends bare metal. Under the shared responsibility model, the hypervisor and guest kernel inside the instance are your responsibility. There is a trade-off I want to name openly: after PwnEscape, Vercel explicitly recommends disabling nested virtualization on sandbox hosts. If you enable it for Kata, you deliberately bring in another virtualization layer with its own attack surface, which has to be kept as current as the kernel.
The list contains Intel instances only. Graviton families such as c7g, m7g or r7g are missing, so if you want to run Kata there, you still depend on bare metal instances. For clusters on Graviton, gVisor is therefore often the more obvious route, because it does not require hardware virtualisation.
Dedicated node pools for code you do not trust
Where no sandbox is possible, what matters is what an attacker finds after the escape. Root on a node means access to all pods on that node and to the kubelet's identity. The Kubernetes Node authorizer, however, only lets the kubelet read secrets, ConfigMaps and volumes of the pods bound to its node, and the NodeRestriction admission plugin prevents it from modifying other Node and Pod objects. An escape on a CI node therefore does not automatically reach your production database credentials, as long as the two never run on the same node. You enforce this separation with taints on dedicated node pools whose matching tolerations only CI jobs or tenant workloads carry. Check that your API server actually has the Node authorizer and NodeRestriction enabled. On EC2 this includes a narrowly scoped instance role for these nodes, because whoever has root on the host reaches it too. As a short-term option, bex.co also mentions a node pool on an unaffected kernel, with the drawback that this kernel is older.
Blocking AF_UNIX via seccomp for individual workloads
A custom seccomp profile can block Unix sockets, but only coarsely. According to the kernel documentation, seccomp filters only see the arguments of a system call and may not dereference pointers. The address family is the first argument of socket() and socketpair(), a plain number, and can therefore be filtered. The control message carrying SCM_RIGHTS, by contrast, sits in a structure in the process's memory, of which sendmsg() only receives a pointer. With seccomp you therefore cannot specifically forbid passing descriptors, only Unix sockets as a whole.
That breaks many programs, however. Name resolution via NSS, database clients using local sockets, process managers, logging and many language runtimes need Unix sockets, and so, often, does PHP-FPM behind a web server in the same pod. For a single, tested service that demonstrably works without them, a profile with seccompProfile.type: Localhost and localhostProfile is an option. As a fleet-wide measure it is not suitable.
Patch speed as hardening
Seven weeks passed between the upstream fix and the public exploit, and some distributions had still not caught up afterwards. For kernel flaws, the path from the advisory to the running kernel on every node is therefore a security measure in itself. On Amazon Linux 2023, Kernel Live Patching belongs switched on and monitored, because it bridges the time until the next maintenance window. You shorten the rest of the gap by regularly replacing nodes from current images instead of keeping them alive for a long time.
What does not help here
The default seccomp profiles with RuntimeDefault allow Unix sockets and passing descriptors. Because the Pod Security Standard “restricted” requires exactly this profile, it changes nothing here either. Dropped capabilities and runAsNonRoot do not apply, since the attack needs no privileges. allowPrivilegeEscalation: false sets no_new_privs, which according to the kernel documentation only prevents execve() from granting new privileges and does nothing against a memory bug in the kernel. readOnlyRootFilesystem protects the container's file system, but the attack takes place in kernel memory.
User namespaces and rootless containers do not change exploitability either, according to bex.co, and updates to runc, containerd or Docker do not help because the bug sits in the host kernel. NetworkPolicies and egress restrictions come to nothing as well, as the attack is purely local. A minimal base image such as Wolfi takes tools away from an attacker but contains no kernel. A CVE gate that only scans container images will therefore never find this flaw. Kernel CVEs only show up when you scan the nodes themselves.
You can find more on node isolation, Pod Security and tenant separation in the series TYPO3 on Kubernetes under IT-Grundschutz.
Detection and verification
Two steps tell you whether a node is affected. First, uname -r shows the running kernel, which you compare against your distribution's tracker, ALAS for Amazon Linux and the Debian Security Tracker for Debian. Whether the new collector is in the kernel at all is revealed, according to bex.co, by the command grep -cE ' unix_(add|del)_edges$' /proc/kallsyms. If it returns a value above zero, the graph-based collector is present, and then the tracker's verdict counts, not the version number alone. Because all containers share the kernel, the same source says this check also works from inside an ordinary container, provided the file is not masked there. Inside gVisor or Kata sandboxes, however, it tells you nothing about the host. On Amazon Linux 2023, sudo dnf updateinfo list cves shows you the available fixes and sudo kpatch list the loaded livepatches.
Proving an attack is considerably harder. Neither DepthFirst nor the distributions publish indicators, and a successful exploitation leaves no clear trace. From the nature of the bug I derive one clue: failed attempts at a use-after-free can destabilise the kernel. Oops messages or unexpected node reboots with functions from net/unix/garbage.c in the stack trace therefore deserve a second look, even though they are no proof. More telling is usually what happens after an escape, such as processes on the host that belong to no known service, or requests in the audit log carrying a kubelet's identity that do not come from its node.
If you suspect compromise, treat the node as compromised. Cordon it and secure memory and logs before you reboot it. Then replace it with a fresh node running a fixed kernel instead of repairing it, and rotate all secrets and tokens of the pods that ran there, the kubelet's credentials and, on EC2, the instance role's active sessions.
Recommendation for operators
Act now if your nodes run a kernel with the new collector but without the fix and run code there that you do not fully control. That covers CI runners for external merge requests, multi-tenant clusters and services that execute user code, but also web applications with a large attack surface, where code execution is only one extension flaw away. Apply the livepatch or kernel update immediately, roll out the nodes again, and until then move untrusted code to nodes with a sandbox or a fixed kernel.
Monitoring is enough if your hosts are not affected, for example on Amazon Linux 2 or on kernels before 6.10 without the backport, or if the fix is already running and you have verified it with uname -r and, for livepatches, with kpatch list. You should still decide which workloads need a sandbox, because this flaw is not the first kernel-based escape this year, as GhostLock already showed.
Regardless of the individual case, what I say for every flaw applies: patches and updates belong automated, and for kernel flaws that means node images and host packages. If Renovate or Dependabot propose new versions of your node images and Helm charts in your OpenTofu code as merge requests, and the pipeline rolls the new kernel out to staging nodes first and tests it, a security kernel like this one is a matter of hours rather than weeks. A cooldown for routine updates protects against compromised releases, and security updates are exempt from it. If a stable jump in the kernel brings a breaking change, for example in drivers or networking features, it shows up in testing rather than in production. Updating nodes by hand, on the other hand, quickly means running a kernel with a public exploit for four weeks during holiday season, followed by a bulk update in which every node has to reboot at once.
Frequently asked questions about CVE-2026-80521
I don't use Ubuntu. Am I still affected?+
Yes, if your kernel contains the new collector and has no fix yet. That applies from 6.10 and to 6.1 from 6.1.141 and 6.6 from 6.6.93. The bug is in the upstream kernel, not in Ubuntu.
Why doesn't seccomp block the attack?+
The default profile allows Unix sockets and passing descriptors. A custom profile can only block Unix sockets as a whole, because seccomp cannot read the contents of sendmsg(). That breaks many applications.
Does a minimal image like Wolfi help?+
No. Wolfi has no kernel of its own, every container uses the host's kernel. An image update changes nothing about the flaw, only the node gets patched.
Is the livepatch on Amazon Linux 2023 enough?+
It closes this flaw if it is actually loaded. Check with sudo kpatch list. Livepatches are only provided for three months per kernel, so still plan the reboot into the fixed kernel.
What about Amazon Linux 2?+
According to ALAS, none of the Amazon Linux 2 kernels are affected, neither the core kernel nor the 5.4, 5.10 and 5.15 extras. The new collector is not in them. Amazon Linux 2023, by contrast, is affected and has been fixed since 30 September.
Conclusion
CVE-2026-80521 is a small bug with a long reach. One missing line in the free path of a cache is enough for a process without any privileges to break out of its container. The transferable lesson lies less in the kernel code than in the architecture. Everything that restricts privileges inside a container offers no protection against bugs in system calls that every container may use, and the kernel remains the shared attack surface of all pods on a node. For code you do not trust, you should therefore decide deliberately which isolation class it gets, not just which capabilities. As with GhostLock and PwnEscape, what decides in the end is how long a fixed kernel takes until it runs on every node.
Sources
- Linux kernel: fix commit 594d90519502 “af_unix: Unlink scc_entry in unix_del_edge().”
- Linux stable: backport for 6.1.y (2b6c2842692d)
- Linux stable: backport for 6.6.y (6fda5c51b8e4)
- Linux kernel: commit 4090fa373f0e “af_unix: Replace garbage collection algorithm.”
- kernel.org: Linux 7.3 changelog (18 October 2026)
- kernel.org: Linux 6.18.54 changelog
- DepthFirst: Containers are no longer safe (discoverer's analysis)
- bex.co: CVE-2026-80521 and the patch gap
- OpenCVE: CVE-2026-80521 (CNA data, CVSS, EPSS)
- Amazon Linux Security Center: CVE-2026-80521
- ALAS2023-2026-3146 (kernel)
- ALAS2023-2026-3147 (kernel6.12)
- ALAS2023-2026-3148 (kernel6.18)
- Debian Security Tracker: CVE-2026-80521
- AWS: Kernel Live Patching on AL2023
- man7: unix(7)
- Kernel docs: Seccomp BPF
- Kernel docs: No New Privileges Flag
- gVisor: Security Model
- gVisor: containerd Quick Start
- Kubernetes docs: Runtime Class
- Kubernetes docs: Node Authorization
- Kata Containers
- AWS: Nested virtualization on EC2
- AWS Containers Blog: Kata Containers on EKS
- GitLab Runner: Kubernetes executor
- Chainguard: Wolfi Overview
I check which of your nodes run untrusted code on a vulnerable kernel, set up sandbox runtimes for CI and tenants, and automate the kernel rollout down to the last node.
Kernel and node audits for Kubernetes and EC2, gVisor and Kata RuntimeClasses for GitLab runners and tenant workloads, livepatching and automated node rotation with tested pipelines.
Platform operations instead of consulting on paper: I continuously check, patch and harden your infrastructure.
