CVE-2026-98811 Overview
CVE-2026-98811 is a race condition in the Linux kernel's net/sched: cls_api traffic classifier subsystem. Two concurrent tc filter add requests targeting the same chain and priority on an unlocked classifier can both fail while leaving an empty tcf_proto structure linked in the chain. The residual proto holds references to the chain, block, and classifier module until teardown.
The flaw is a follow-up to commit d4e359b3608a, which addressed the related insert-race teardown issue but left the empty-proto residual unresolved. Local users with CAP_NET_ADMIN (including namespace-local via unshare -Urn) can trigger the condition.
Critical Impact
A local attacker with CAP_NET_ADMIN in a user namespace can repeatedly leak kernel references, pinning the classifier module and consuming kernel resources until the chain or block is torn down.
Affected Products
- Linux kernel builds with CONFIG_NET_CLS_FLOWER=y and unlocked classifiers
- Linux kernel versions prior to the fixes referenced in the stable commits 0bb8eab29b55, 0fc74e7d97d2, 315e4fe65cf0, 570141625d21, and ebc9611ab154
- Container and multi-tenant hosts that permit unprivileged user namespaces
Discovery Timeline
- 2026-10-18 - CVE-2026-98811 published to NVD
- 2026-10-21 - Last updated in NVD database
Technical Details for CVE-2026-98811
Vulnerability Analysis
The Linux traffic control (tc) classifier API maintains per-chain, per-priority tcf_proto instances that hold filter handles in an IDR (ID radix tree). On unlocked classifiers, two tc filter add requests on the same chain and priority can race through change() on the shared proto.
When both requests fail (for example in fl_hw_replace_filter after publishing their handle), the winner's tcf_chain_tp_delete_empty() attempt aborts because the loser's handle is still registered in the IDR. The loser's error path then drops only its own reference without re-attempting reclamation. The empty proto remains linked in the chain, pinning a chain reference, a block reference, and the classifier module reference.
The condition is observable as a bare filter protocol ip pref 1 flower chain 0 header in tc filter show output with no filter entries. Repeated triggering accumulates leaked state over time.
Root Cause
The root cause is a race condition [CWE-362] in the error-path reclamation logic of cls_api.c. The emptiness check that governs proto unlinking does not run a second time after a failed insert, so a proto that becomes empty only after the losing request releases its handle is never reclaimed.
Attack Vector
Exploitation requires local access with CAP_NET_ADMIN within the target network namespace. An unprivileged user can obtain this capability via unshare -Urn. The attacker creates a veth pair, attaches an ingress qdisc, and issues two concurrent tc filter add ... flower skip_sw ... action drop commands in a loop. Each failed race leaks kernel references and keeps the classifier module pinned.
The vulnerability does not permit code execution. Impact is limited to resource exhaustion and module reference pinning.
Detection Methods for CVE-2026-98811
Indicators of Compromise
- Empty filter headers in tc filter show dev <iface> ingress output, specifically filter protocol ip pref N flower chain 0 entries with no attached filter rules
- Growing kernel memory usage attributable to net/sched allocations without corresponding active filters
- Classifier modules such as cls_flower showing elevated reference counts in /proc/modules that cannot be unloaded
Detection Strategies
- Audit tc filter state on hosts running untrusted workloads and alert on empty proto headers that persist across polling intervals
- Correlate repeated failures from fl_hw_replace_filter in kernel logs with concurrent tc filter add command executions by the same UID or namespace
- Monitor for processes invoking unshare -Urn followed by rapid tc command bursts, which is characteristic of the reproducer
Monitoring Recommendations
- Enable auditd rules on execve events for /sbin/tc and /usr/sbin/tc to capture command arguments and parent process context
- Track net/sched subsystem slab usage over time using /proc/slabinfo to identify abnormal growth in tc-related caches
- Ingest kernel logs into a centralized SIEM and alert on repeated classifier offload failures correlated with namespace creation events
How to Mitigate CVE-2026-98811
Immediate Actions Required
- Apply the upstream Linux kernel patches referenced in the stable commits and reboot affected systems
- Restrict unprivileged user namespace creation on multi-tenant hosts by setting kernel.unprivileged_userns_clone=0 or equivalent sysctl where supported
- Audit which workloads require CAP_NET_ADMIN and remove the capability from containers and services that do not need it
Patch Information
The fix reclaims the proto on the error path of any failed request that holds a proto reference. Reclamation is emptiness-gated through tcf_chain_tp_delete_empty(), which unlinks the proto only when delete_empty() confirms it is empty. Protos created by the failing request itself are reclaimed unconditionally since the request is the only owner. See the upstream commits: 0bb8eab29b55, 0fc74e7d97d2, 315e4fe65cf0, 570141625d21, and ebc9611ab154.
Workarounds
- Disable or avoid use of unlocked classifiers with hardware offload (skip_sw) on hosts exposed to untrusted local users until the patch is applied
- Use seccomp or AppArmor profiles to block the unshare syscall for workloads that do not require namespace manipulation
- Monitor and periodically tear down affected chains or blocks on hosts where leaked empty protos are observed
# Restrict unprivileged user namespace creation (Debian/Ubuntu)
sysctl -w kernel.unprivileged_userns_clone=0
echo 'kernel.unprivileged_userns_clone=0' >> /etc/sysctl.d/99-harden.conf
# Drop CAP_NET_ADMIN from container runtimes where not required
# Example for Docker:
docker run --cap-drop=NET_ADMIN <image>
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

