How Kernel TLS Moves Encryption Into the Kernel and What That Exposes
🛡️ Security Intermediate 6 min read

How Kernel TLS Moves Encryption Into the Kernel and What That Exposes

Kernel TLS trades a process boundary for throughput: the record parser that handles every byte a remote peer sends runs in ring 0. Here is what that buys, what it costs, and how to tell if you use it.

Published: September 19, 2026 • Updated: September 19, 2026
kernel tlslinux kerneltlsattack surface

Most engineers first meet kernel TLS as a checkbox in a web server config or a NIC datasheet: turn it on, watch CPU usage drop. That framing hides what the feature actually does, which is move the entire TLS record layer, including decryption of attacker-supplied bytes, out of a sandboxed userspace process and into ring 0. CISA's addition of the kTLS receive-path bug CVE-2025-39682 to its exploited-vulnerabilities catalog in September 2026 is a good moment to understand the trade.

What the kernel takes over

The kernel's own documentation describes TLS as an "Upper Layer Protocol (ULP) that runs over TCP" providing "end-to-end data integrity and confidentiality." In the conventional model, a library such as OpenSSL does everything: the handshake, key derivation, and the symmetric encryptionEncryption🛡️The process of converting data into a coded format that can only be read with the correct decryption key. and decryption of every application record. The kernel just sees an opaque byte stream.

Kernel TLS splits that job. The handshake still happens in userspace, because it is complex, stateful and changes with every protocol revision. Once the session keys exist, the application hands them to the kernel by attaching the TLS ULP to the connected TCP socket and then setting the transmit and receive crypto parameters through separate socket options. From then on, the application reads and writes plaintext through ordinary socket calls and the kernel performs the symmetric crypto on the way in and out.

The kernel documentation distinguishes two modes. In software mode, counted by the `TlsCurrTxSw` and `TlsCurrRxSw` statistics, the host CPU does the crypto inside the kernel. In device mode, counted by `TlsCurrTxDevice` and `TlsCurrRxDevice`, a capable NIC does it. Either way, the code that parses TLS record headers and decides how to queue and copy decrypted data lives in the kernel's networking stack. For the software receive path, that file is `net/tls/tls_sw.c`, and that is where CVE-2025-39682 was found.

Why anyone wants this

Three reasons, all legitimate.

**Zero-copy sends.** With TLS in the kernel, a server can hand a file descriptor to the socket and let the kernel encrypt straight from the page cache. Without it, the file has to be read into userspace, encrypted there, and written back down. For static content and object storage at scale, that copy is a measurable cost.

**Hardware offload.** Device mode lets a NIC encrypt and decrypt at line rate. That only works if the kernel owns the record layer, because the NIC needs the keys and the record boundaries.

**Simpler application code.** Anything built on plain socket I/O can gain TLS without linking a crypto library into its data path. This matters for storage protocols and in-kernel services that were never designed around a userspace TLS stack.

The organisations most likely to have kTLS receive enabled are therefore the ones running high-throughputThroughput📖Actual amount of data successfully transferred over a connection, often lower than bandwidth. edge proxies, CDN nodes, storage gateways and custom network daemons. A default distribution install with a standard web server usually does not, even though the module is available.

What it exposes

Here is the trade. A TLS record parser has to handle hostile input by design: every byte after the handshake comes from the remote peer. When that parser runs in a userspace process, a bug in it is contained by the process boundary, by seccomp, by containers and by the fact that the process usually runs as an unprivileged user. Memory corruption in a userspace TLS library is bad, but it is one process.

When the same parser runs in the kernel, there is no boundary. A logic error in how records are typed and queued becomes a kernel memory-safety issue, and kernel memory-safety issues become root. The exploitExploit🛡️Code or technique that takes advantage of a vulnerability to cause unintended behavior, such as gaining unauthorized access. write-up that accompanied CVE-2025-39682's KEV listing shows exactly this path: a record-type confusionType Confusion🛡️A memory-safety bug (CWE-843) in which code accesses a resource using an incompatible type, such as reading a pointer as an integer. In JavaScript engines it typically arises when optimized code trusts an assumption about an object's shape that a callback has since invalidated, as in CVE-2026-85046. in the receive loop is turned into a use-after-free, then into a page-level use-after-free by spraying socket buffers, then into an arbitrary write that overwrites the kernel's `core_pattern` setting to gain root. The author reported roughly 79 percent reliability. That is the difference between "a TLS library crashed" and "the host is gone."

Red Hat's advisory for the bug makes the exposure condition precise: it "only affects systems with kernel TLS (CONFIG_TLS) enabled and the TLS ULP attached to TCP sockets." Both halves matter. The module being present is not the same as it being used, and it is the attachment to a live socket that puts the vulnerable code on the path of untrusted data.

Local or remote?

The scoring disagreement over CVE-2025-39682 is instructive. The Linux CNA rated it 9.8 with a network attack vector. NVD rated it 7.1 local. Red Hat rated it 7.0 with a high-complexity network vector. All three are looking at the same code.

The local reading reflects the demonstrated exploit, which drives the bug from a local process that controls both ends of the socket. The network reading reflects the architecture: the receive path exists to process records sent by a remote peer, so a remote trigger is at least plausible even if nobody has published one. When you are deciding whether a kTLS bug matters to a given host, the practical question is whether any listening service on it attaches the TLS ULP to sockets that accept connections from networks you do not control. If yes, treat the network reading as the planning assumption.

What to do with this

If you did not know whether your fleet uses kernel TLS before reading this, that is the first thing to fix. Check the configuration of your web servers, proxies and storage daemons for kTLS options, and check for the kernel's TLS statistics counters on hosts where the module is loaded. Where kTLS is enabled on internet-facing services, those hosts belong at the front of the kernel-update queue, ahead of hosts where the module merely exists.

Where you cannot patch quickly, Red Hat's documented mitigation is to prevent the `tls` module from loading, which disables kernel TLS entirely and pushes the crypto back into userspace libraries. That is a real functional change with a performance cost, so read our guide on how to blacklist a kernel moduleKernel Module🛡️A piece of kernel code (driver, filesystem, protocol or crypto component) that is loaded into a running Linux kernel on demand rather than compiled in. Blocking a vulnerable module from loading is a common stopgap mitigation until a patched kernel can be booted. as a stopgap mitigation before applying it in production.

Finally, be honest about the trade you are making when you enable the feature. Kernel TLS is a performance optimisation that widens the kernel's attack surfaceAttack Surface🛡️The sum of all points where an unauthorized user could attempt to enter or extract data from a system: exposed services, interfaces, accounts, and integrations. Reducing attack surface means removing reachability, not just patching. to include every TLS record from every peer. For a CDN edge that is a rational bet. For an internal service that could get the same result from a userspace library, it may not be, and the three-day deadline attached to this bug is a preview of what the ongoing maintenance cost looks like. Our planning piece on why the three-day KEV deadline changes how you plan kernel reboots covers that side of the problem.