Linux Kernel TLS Race Fix CVE-2026-23240: Use disable_delayed_work_sync

  • Thread Author
The Linux kernel has received a small but important patch that fixes a timing (race) bug in the kernel TLS implementation: CVE-2026-23240 addresses a race in tls_sw_cancel_work_tx() where a worker can be scheduled after the kernel believes the delayed work has been cancelled, allowing the worker to dereference a freed TLS object. The fix is a surgical change that replaces cancel_delayed_work_sync() with disable_delayed_work_sync() to both drain and prevent re-queueing of the delayed work item. (spinics.net)

Neon Linux penguin logo glows on a server rack amid TLS labels and network cables.Background​

The Linux kernel's in-kernel TLS (often referred to as kTLS) offloads certain cryptographic operations from userspace into the kernel to improve performance for high-throughput networking paths. The kTLS transmit path maintains delayed work to perform deferred transmission tasks and flushes; the subsystem uses a workqueue-backed worker (tx_work) and bitflags to track scheduling and to coordinate teardown on socket close.
The problematic function, tls_sw_cancel_work_tx(), is called during socket shutdown to cancel outstanding TX work and prepare the TLS TX context for release. Historically, cancel_delayed_work_sync() has been used widely to cancel delayed_work and wait for a running instance to finish. However, in complex race scenarios—where another CPU may set scheduling bits and re-schedule that same delayed work from interrupt- or softirq-context (for example, from a Delayed ACK handler or ksoftirqd)—the plain cancel_delayed_work_sync() can leave a window where scheduling can succeed even after cancel returns, causing the worker to run against freed memory. The kernel documentation describes both cancel and disable semantics; importantly, disable_delayed_work_sync() increments a disable count and prevents further queueing while draining any running instance of the work.
This change was discovered during a code audit of the kernel's net/tls code and submitted upstream as a focused patch that swaps the cancel API for the disable API to remove the race window. The upstream commit and related downstream stable inclusion were applied in late February 2026. (spinics.net)

What changed: the patch and its intent​

The one-line defensive change​

The upstream patch replaces a single API call:
  • Removed: cancel_delayed_work_sync(&ctx->tx_work.work);
  • Added: disable_delayed_work_sync(&ctx->tx_work.work);
That one-line swap enforces two behaviors atomically from the perspective of the TLS context teardown routine:
  • It cancels and drains any currently pending or executing delayed work.
  • It disables new submissions of the delayed work, so attempts to schedule the work after this point will fail.
The change is intentionally minimal because the underlying problem is not algorithmic complexity but an ordering/race between cancellation and scheduling. By switching to the disable API, the code blocks the re-queue path that was creating the race. (spinics.net)

Why disable instead of cancel is necessary​

  • cancel_delayed_work_sync() cancels the work and waits for an already-running instance to finish, but it does not prevent subsequent queue attempts from succeeding after cancel returns.
  • disable_delayed_work_sync() performs three actions: it increments a disable counter so attempts to queue the work will fail, cancels if it was pending, and waits for any executing instance to complete or drain. This semantics closes the re-queue gap that in practice allowed tx_work_handler() to be scheduled after the socket close started teardown. The kernel's workqueue API documents these behaviors and the rationale for choosing disable in teardown flows where the object holding the work is about to be freed.

Technical analysis: the precise race​

The race scenario (CPU interleaving)​

The commit message lays out a simple but convincing interleaving between two CPUs:
  • CPU0: executes tls_sk_proto_close() → tls_sw_cancel_work_tx() → calls cancel_delayed_work_sync() and proceeds to release resources and free the TLS object.
  • CPU1: concurrently runs code triggered by normal network processing (tls_write_space() → tls_sw_write_space()) that checks BIT_TX_SCHEDULED and, finding it clear, attempts to set the scheduled bit and call schedule_delayed_work(&tx_ctx->tx_work.work, 0) to queue the tx work.
If scheduling runs after cancel_delayed_work_sync() has returned but before the TLS object is actually destroyed, the scheduled worker (tx_work_handler) may run on CPU1 or on the workqueue and access fields in the tx context that another CPU is actively tearing down — creating a classic use-after-free or dereference-of-freed-object scenario. That can cause kernel crashes, hung work, or undefined behavior, depending on timing. (spinics.net)

Why this matters in TLS code​

Kernel TLS interacts with network stack callbacks that can be triggered from softirq contexts, delayed ACK timers, or ksoftirqd. These paths are asynchronous relative to userspace close() or protocol-level teardown. The TX work is inherently asynchronous and designed to be re-armed by many transmit-related events. That design means any cancellation path must not only wait for in-flight callbacks to finish but also prevent new callbacks from successfully re-arming the same work item after cancellation has completed.
Past kernel fixes in similar subsystems have used disable_delayed_work_sync() for exactly this reason: to prevent re-queue-after-cancel races that lead to use-after-free bugs when the parent object is freed shortly after cancellation. The change for CVE-2026-23240 follows that established pattern.

Impact and exploitability​

What the vulnerability allows (practical consequences)​

  • The immediate technical effect is that the tx_work_handler() worker may dereference a TLS object that has been freed or is in the process of being freed. The consequences range from:
  • kernel oops/panic or crash,
  • hung tasks (observed historically in syzkaller reports from similar code paths),
  • potential denial-of-service (availability impact),
  • in worst-case scenarios a latent use-after-free could be weaponized by a determined local attacker to achieve memory corruption. However, the latter requires very specific timing and memory layout conditions and is harder to turn into reliable code execution.
  • CVSS and exploitation metrics published by industry trackers characterize the issue as medium severity with a CVSS v3 base score around 4.7 and a very low EPSS (probability-of-exploitation) score, reflecting limited real-world exploitability and the requirement for local/adjacent code paths to be present. These scoring products report this as a localized availability-impacting bug, not a remote RCE. (tenable.com)

Historical context: TLS and kernel races​

This is not the first time kernel TLS or related async-kernel-work interactions have produced race conditions. Prior CVEs in the kernel TLS area highlighted subtle races between asynchronous work scheduling and object teardown (for example, in 2024 there were CVEs that exposed similar hazards in the TLS subsystem). Those earlier issues demonstrated that a single missed disable or ordering problem can create use-after-free or deadlock conditions that are sometimes triggered only under heavy load or fuzzing. The current fix is consistent with the kernel community’s approach of using disable-and-drain APIs where objects are freed soon after work cancellation.

How vendors and distributions handled the fix​

The patch was accepted upstream and was carried into the stable kernel updates series; stable patch notices indicate that the change was backported to stable trees where necessary. The upstream commit referenced in the patch was created and merged during the 2026 kernel maintenance window and appears on stable branch feeds shortly thereafter. For administrators this means that the fix will appear in vendor kernels when maintainers pick up the stable updates — but timing depends on the distro's stable update cadence. Confirmed commit notes and stable-list postings show the fix labeled for the 6.19 stable maintenance track and listed in the stable patch rollout. (spinics.net)
Important caution: I could not find an authoritative vendor advisory listing exact packaged versions that include this fix across all distributions. That is normal — distribution vendors publish distribution-specific advisories (e.g., Debian, Ubuntu, Red Hat, SUSE) and the timetable varies. Until you see a distro advisory or updated kernel package for your platform, you should treat kernels as unpatched even if upstream contains the fix. If you use vendor kernels, monitor your vendor's security pages for the patch to arrive or apply vendor-supplied backports. If you run custom kernels, the upstream stable commit can be applied directly. (spinics.net)

Practical steps for sysadmins, developers, and packagers​

If you run distribution kernels (recommended)​

  • Check whether your distribution has published a security advisory that includes a stable kernel update containing the commit. If they have, check package changelogs or advisory text for mentions of net/tls or "tls_sw_cancel_work_tx".
  • Apply the vendor-supplied kernel package update as soon as your testing and change-control processes allow. The risk here is primarily availability; for production systems running high-volume TLS workloads, prioritize the update.
  • If you cannot immediately update, consider isolating or reducing in-kernel TLS use where possible (e.g., temporarily disable kTLS for workloads that permit it) until your kernel packages are upgraded. That is not always practical but can be a mitigation where kTLS is optional. (cvedetails.com)

If you maintain custom or upstream kernels​

  • Pull or cherry-pick the upstream stable commit that replaces cancel_delayed_work_sync() with disable_delayed_work_sync() in net/tls/tls_sw.c. The commit is referenced in stable patch lists and kernel.org change logs; the stable-list messages show the upstream commit ID and date.
  • Build and deploy a test kernel and validate under your workload. Pay special attention to:
  • TLS-heavy traffic patterns,
  • delayed ACK behavior,
  • any kernel crash/hang or dmesg entries referencing tls_sw_cancel_work_tx or tx_work_handler.
  • For long-lived production kernels with strict stability requirements, coordinate back-porting carefully and include the one-line change in your vendor branch, then run regression tests for net/ and TLS-related functionality. (spinics.net)

How to confirm the system is affected and patched​

  • Check your kernel version: uname -r. That helps you map your running kernel to vendor advisory lists.
  • Inspect kernel changelogs, packaged kernel changelogs, or vendor advisory metadata for mentions of the patch (net/tls: Fix race condition in tls_sw_cancel_work_tx or similar text).
  • Look for dmesg log lines showing hangs or oopses referencing tls_sw_cancel_work_tx or tx_work_handler; fuzzing and syzkaller runs historically reported hung tasks with those stack traces. If you see such messages in logs during normal operation, treat them seriously and schedule remediation.

Detection, forensics, and troubleshooting​

  • Kernel logs (dmesg / syslog) are the first stop. Look for messages like "INFO: task hung in tls_sw_cancel_work_tx" or stack traces that include net/tls/tls_sw.c and tx_work_handler call sites. Syzkaller tickets and prior bug reports show these diagnostic messages occurring when the race manifested as hung tasks.
  • If you reproduce a crash or hang, capture a vmcore and obtain the stack trace; kernel oops text nearly always reveals the implicated functions because the worker will be on the stack when it dereferences freed structures.
  • For forensic confirmation of a use-after-free exploit, you will likely need a memory capture and a detailed analysis comparing allocation/free timelines; accidental crashes and hung tasks are the more common real-world outcome, and those are diagnosable via logs and crash dumps.

Risk prioritization and recommended timeline​

  • Severity: trackers assign this CVE a medium severity with CVSS v3 ≈ 4.7, reflecting local attack vector and availability-centered impact rather than remote code execution. EPSS and exploitability indicators are very low at publication time, and the issue originates from a code audit rather than observed exploitation in the wild. These indicators place this CVE as an important but not emergency-level update for most organizations. (tenable.com)
  • Recommended action window:
  • Network and security teams on high-throughput TLS endpoints (load balancers, proxies, appliances using kTLS): schedule testing and deployment within days, because a crash or hang on those hosts would be disruptive.
  • Generic servers running kTLS less intensively: deploy during your next patch window (1–2 weeks); keep this tracked as a medium-priority kernel update.
  • Systems where kTLS is not used: lower priority, but remain aware since package updates may be included in general kernel rebuilds. (cvedetails.com)

Developer takeaways and code hygiene​

This CVE is an instructive example of a recurring theme in kernel concurrency bugs:
  • APIs that wait for a running worker to finish are not always sufficient when other CPUs can re-queue work after the wait completes. The correct semantic in teardown paths where the object is to be freed is often to disable the work item — preventing further queue attempts — and drain any running instances. The kernel workqueue API provides that semantics via disable_work_sync()/disable_delayed_work_sync().
  • Small, localized changes guided by a clear understanding of API semantics are often the safest and fastest route to a correct fix. In this case, the one-line swap removes the ordering window without adding complex locking.
  • Defensive programming for asynchronous callbacks should treat any cancellation path as a two-stage problem: (1) stop new queue attempts and (2) wait for in-flight work to finish. Doing only the second step leaves a re-queue window.

What I checked and what remains unverifiable​

  • Verified: upstream commit message, exact patch change (one-line swap), stable-list inclusion, and tracking entries on mainstream vulnerability databases. The kernel mailing-list and stable patch notices include the commit and the explanation of the race that motivated the change. (spinics.net)
  • Verified: kernel workqueue API semantics for cancel vs disable semantics using the kernel documentation and prior kernel fixes that applied the same pattern.
  • Verified: CVE catalog entries and scoring metadata (CVSS, EPSS) from reputable trackers (Tenable, CVEdetails/NVD mirrors). These confirm the “medium” severity and low exploitability assessments recorded when the CVE was published. (tenable.com)
  • Not fully verifiable in the public record at the time of writing: exact distribution package names and the precise kernel-package versions that include the backported fix for all major vendors. That information is published by each vendor on their own cadence; you must check your distribution's advisory pages or package changelog to determine whether your installed kernel contains the fix. Treat un-updated vendor kernels as unpatched until you confirm otherwise. (spinics.net)

Summary and final recommendations​

CVE-2026-23240 is a deliberate, low-complexity fix for a subtle race in the Linux kernel's TLS transmit teardown path. The problem was discovered in a code audit, and the fix is a responsible API-semantics change: replace cancel_delayed_work_sync() with disable_delayed_work_sync() in tls_sw_cancel_work_tx() to avoid re-queue-after-cancel races that can result in worker threads accessing freed TLS objects. The patch is upstream and present in stable kernel updates; trackers rate the issue medium and exploitability low. (spinics.net)
Action checklist for operators:
  • Confirm whether your vendor has shipped an updated kernel that includes the fix; if so, plan and apply that update promptly. (cvedetails.com)
  • If you maintain custom kernels, apply the upstream stable commit, rebuild, and test the new kernel before deployment. (spinics.net)
  • Monitor kernel logs for any messages hinting at hung tasks or oopses involving tls_sw_cancel_work_tx and treat them as an operational signal to prioritize patching.
This CVE serves as a reminder that asynchronous work and object lifetime management are intrinsically tricky in kernel code — and that the kernel's workqueue API contains the right primitives (disable-and-drain) to avoid exactly this class of bug.

Source: MSRC Security Update Guide - Microsoft Security Response Center
 

Back
Top