CVE-2026-98536 Overview
CVE-2026-98536 is a Linux kernel denial-of-service condition in the writeback subsystem. The cleanup_offline_cgwbs_workfn() worker drains a dying cgroup writeback domain (cgwb) by repeatedly calling cleanup_offline_cgwb() with a cond_resched() between passes. On CONFIG_PREEMPTION kernels, that cond_resched() is a no-op, so the drain loop never reports a Tasks-RCU quiescent state. A cgwb with millions of attached inodes can run for minutes, blocking any caller of synchronize_rcu_tasks() such as BPF or ftrace detach paths. The result is hung tasks and, in observed cases, a kernel panic triggered by the hung task detector.
Critical Impact
Long cgwb drains stall Tasks-RCU grace periods, blocking BPF or ftrace teardown and causing hung-task panics on arm64 and x86 systems using PREEMPT_LAZY or full preemption.
Affected Products
- Linux kernel builds with CONFIG_PREEMPTION enabled
- Architectures with PREEMPT_LAZY support, including arm64 and x86
- Linux 6.18 confirmed in the reported reproduction
Discovery Timeline
- 2026-10-18 - CVE-2026-98536 published to NVD
- 2026-10-19 - Last updated in NVD database
Technical Details for CVE-2026-98536
Vulnerability Analysis
The writeback subsystem tracks per-cgroup writeback domains, known as cgwbs. When a cgroup goes offline, cleanup_offline_cgwbs_workfn() schedules a worker that iteratively calls cleanup_offline_cgwb() until it returns false. Between passes the worker calls cond_resched() to yield the CPU.
On a CONFIG_PREEMPTION kernel, _cond_resched() returns zero. Under PREEMPT_DYNAMIC, the full and lazy preemption modes disable it entirely. Since commit 7dadeaa6e851 restricted preemption models, full and lazy are the only options on arm64 and x86 with PREEMPT_LAZY.
As a result, the drain loop never reports a Tasks-RCU quiescent state. A worker draining a cgwb that holds millions of inodes can run uninterrupted for minutes. In one reported case on a 6.18 arm64 host in lazy mode, the worker ran for more than 11 minutes on a single dying cgroup.
Root Cause
The drain loop depends on cond_resched() to announce quiescent states for Tasks-RCU. On preemptible kernels that call is a no-op, so Tasks-RCU grace periods started by synchronize_rcu_tasks() cannot complete while the worker runs. This is a kernel-level denial-of-service through CPU and RCU stall rather than a memory safety flaw.
Attack Vector
An attacker or heavy workload that creates and destroys cgroups holding large numbers of inodes can trigger extended drain loops. Any concurrent operation that requires a Tasks-RCU grace period inherits the drain duration. Observed trigger paths include BPF program unlink through bpf_trampoline_unlink_prog to unregister_ftrace_direct, and ftrace detach operations. In the reported scenario, 42 tasks queued in D state behind the trampoline mutex and the hung task detector fired at 614 seconds, panicking the host.
See the upstream commits referenced in the Linux kernel stable tree for the full fix. The patch replaces the plain cond_resched() with cond_resched_tasks_rcu_qs() and wraps the call in a do { } while loop to cover the case where many small cgroups cause cleanup_offline_cgwb() to return false on the first iteration.
Detection Methods for CVE-2026-98536
Indicators of Compromise
- Kernel worker threads associated with cleanup_offline_cgwbs_workfn running for several minutes or longer on hosts with heavy cgroup churn.
- Hung task detector warnings referencing synchronize_rcu_tasks, bpf_trampoline_update, or unregister_ftrace_direct in dmesg.
- Growing queues of tasks in uninterruptible (D) state blocked on the BPF trampoline mutex or ftrace shutdown paths.
Detection Strategies
- Monitor kernel logs for INFO: task ... blocked for more than messages tied to BPF or ftrace teardown routines.
- Track long-running kworker threads and correlate with cgroup deletion events and inode counts per cgroup.
- Alert on panics or soft lockups immediately following BPF program unload or ftrace detach operations.
Monitoring Recommendations
- Collect kernel version, preemption model, and architecture inventory to identify hosts exposed to the PREEMPT_LAZY path.
- Instrument cgroup lifecycle events and writeback inode counts to flag cgroups that may generate multi-minute drains.
- Capture dmesg and /proc/sysrq-trigger t snapshots when hung task warnings fire to confirm the signature.
How to Mitigate CVE-2026-98536
Immediate Actions Required
- Apply vendor kernel updates that incorporate the upstream fix for the cgwb drain loop as soon as they are available.
- Audit production hosts running Linux 6.18 or newer on arm64 and x86 with PREEMPT_LAZY enabled, since those are the confirmed affected configurations.
- Avoid detaching BPF programs or ftrace probes during heavy cgroup teardown windows until patched.
Patch Information
The fix calls cond_resched_tasks_rcu_qs() inside a do { } while loop so each drain pass reports a Tasks-RCU quiescent state, even when cleanup_offline_cgwb() returns false early. The upstream commits are available in the stable tree, including commit 863049b, commit 260f586, commit 407a5d2, commit 42462b5, commit 4eb8896, commit ebf1555, and commit fd3bf32.
Workarounds
- Where feasible, boot with a non-lazy preemption mode so cond_resched() yields and reports quiescent states.
- Limit the number of inodes retained per cgroup and stagger cgroup deletions to shorten individual drain passes.
- Defer BPF trampoline and ftrace detach operations until writeback drain workers for recently deleted cgroups have completed.
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

