CVE-2026-63978
Critical · 9.8CVSS
9.8
EPSS
0.5%
Published
Jul 19, 2026
CISA due date
—
Cloudanix analysis
A mistake in Linux kernel network cleanup code can leave behind leaked network connections and cause crashes when network setups are shut down.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Moderate
- Impact
- Full Compromise
- Component
- OS kernel
- Class
- Race Condition
- Remediation
- Patch
- Effort
- Medium
- Patch risk
- Low
What it actually does
Reversed arguments to list_splice_init in handshake_net_exit leave pending handshake requests attached to sockets and allocations when a network namespace exits; the subsequent race between drain and handshake_req_cancel produces list corruption or use-after-free when concurrent cancels occur during netns teardown.
Why it matters to the business
Unpatched kernels experience socket and memory leaks on namespace destruction, potential kernel crashes or memory corruption under load from TLS handshakes or RPC, and elevated risk of denial of service in containerized or multi-tenant network environments.
How an attack unfolds
An attacker with the ability to create network namespaces and trigger TLS handshakes (for example via sunrpc or kernel TLS sockets) forces repeated netns creation and exit while pending handshake requests exist; concurrent cancel paths from timeouts then race with the broken drain logic, corrupting kernel lists and potentially freeing structures while still referenced.
How to fix it
Upgrade to a kernel containing the corrected splice order, HANDSHAKE_F_NET_DRAINING guard in remove_pending, list_del_init usage in the drain loop, and hr_file pinning under hn_lock; apply the fix from the commit that resolves the reversed arguments and race.
How to confirm the fix worked
Confirm the presence of the corrected list_splice_init call order, the HANDSHAKE_F_NET_DRAINING check, and list_del_init usage in handshake.c; exercise netns exit under concurrent handshake load and verify no pending requests or list corruption remain.
Typical attack path
- Create or enter a network namespace and initiate TLS handshake requests that remain pending
- Trigger net namespace exit while requests are still queued
- Force concurrent handshake_req_cancel operations from timeout paths that do not hold netns references
- Observe list corruption or use-after-free during drain or remove_pending
How to detect it
Review kernel logs for list corruption warnings, slab or socket leaks on netns exit, and crashes involving handshake_req or remove_pending; inspect rhashtable and list operations around net namespace destruction in environments using kernel TLS.
What your security team should do
- Prioritize kernel updates for systems running recent kernels with net/handshake and sunrpc TLS
- Monitor for crashes or excessive socket allocations during container or namespace teardown
- Limit untrusted code from creating network namespaces until patched
The record
In the Linux kernel, the following vulnerability has been resolved: net/handshake: Drain pending requests at net namespace exit The arguments to list_splice_init() in handshake_net_exit() are reversed. The call moves the local empty "requests" list onto hn->hn_requests, leaving the local list empty, so the subsequent drain loop runs zero iterations. Pending handshake requests that had not yet been accepted are not torn down when the net namespace is destroyed; each one keeps a reference on a socket file and on the handshake_req allocation. Pass the source and destination in the documented order (list_splice_init(list, head) moves list onto head) so the pending list is transferred to the local scratch list and drained through handshake_complete(). Fixing the splice direction exposes a list-corruption race. After the splice each req->hr_list still has non-empty link pointers, threading the stack-local scratch list rather than hn_requests. A concurrent handshake_req_cancel() -- for example, from sunrpc's TLS timeout on a kernel socket whose netns reference was not taken -- finds the request through the rhashtable, calls remove_pending(), and sees !list_empty(&req->hr_list). __remove_pending_locked() then list_del_init()s an entry off the scratch list while the drain iterates, corrupting it. The same call arriving after the drain loop has run list_del() on an entry hits LIST_POISON instead. Have remove_pending() check HANDSHAKE_F_NET_DRAINING under hn_lock and report not-found when drain is in progress. The drain has already taken ownership; handshake_complete()'s existing test_and_set on HANDSHAKE_F_REQ_COMPLETED still arbitrates between drain and cancel for who calls the consumer's hp_done. Use list_del_init() rather than list_del() in the drain so req->hr_list does not carry LIST_POISON after drain releases the entry. The DRAINING guard in remove_pending() makes cancel return false, but cancel still falls through to test_and_set_bit on HANDSHAKE_F_REQ_COMPLETED and drops the request's hr_file reference. Without another pin, if that is the last reference, sk_destruct frees the request while it is still linked on the drain loop's local list. Pin each request's hr_file under hn_lock before releasing the list, and drop that drain pin after the loop finishes with the request.
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|