CVE-2026-22976 Overview
A NULL pointer dereference vulnerability exists in the Linux kernel's Quick Fair Queueing (QFQ) network scheduler subsystem within the sch_qfq module. The vulnerability occurs during the qdisc reset operation when deactivating an inactive aggregate, leading to a kernel NULL pointer dereference that can crash the system.
The flaw stems from an incorrect assumption in the qfq_reset function where qfq_class->leaf_qdisc->q.qlen > 0 does not necessarily imply that the class itself is active. When two qfq_class objects point to the same leaf_qdisc (which can occur when a QFQ qdisc is attached as the root qdisc while another QFQ qdisc is pending destruction), the shared leaf_qdisc->q.qlen may be non-zero while the second qdisc's own queue length is zero, causing qfq_reset() to wrongly attempt deactivation of an inactive aggregate.
Critical Impact
This vulnerability can cause a kernel panic and system crash through a NULL pointer dereference in qfq_deactivate_agg, leading to denial of service conditions on affected Linux systems.
Affected Products
- Linux Kernel (multiple stable branches affected)
- Systems using QFQ (Quick Fair Queueing) network scheduler
- Linux distributions with sch_qfq module enabled
Discovery Timeline
- 2026-01-21 - CVE CVE-2026-22976 published to NVD
- 2026-01-21 - Last updated in NVD database
Technical Details for CVE-2026-22976
Vulnerability Analysis
This NULL pointer dereference vulnerability resides in the Linux kernel's traffic control (tc) subsystem, specifically in the QFQ (Quick Fair Queueing) scheduler implementation located in net/sched/sch_qfq.c. The vulnerability manifests during qdisc destruction when the qfq_reset_qdisc() function is invoked.
The QFQ scheduler maintains aggregate structures that group classes for scheduling purposes. When a qdisc is being destroyed (via qdisc_put() and qdisc_destroy()), the qfq_reset() function iterates through classes and attempts to deactivate aggregates based on the queue length of the leaf qdisc. However, the code fails to properly validate whether the aggregate is actually active before attempting deactivation.
The crash occurs because multiple qfq_class objects can reference the same leaf_qdisc in specific scenarios:
- One QFQ qdisc is attached as the root qdisc
- Another QFQ qdisc is temporarily referenced (e.g., via qdisc_get()/qdisc_put()) and is pending destruction
When packets flow through the root QFQ qdisc, the shared leaf_qdisc->q.qlen increases. Simultaneously, if the second QFQ qdisc triggers destruction with its own q->q.qlen == 0, the qfq_reset() function sees a non-zero leaf_qdisc->q.qlen and incorrectly attempts to deactivate an aggregate that was never activated, resulting in a NULL pointer dereference in qfq_deactivate_agg().
Root Cause
The root cause is a logic error in the condition check within qfq_reset() that determines whether to deactivate an aggregate. The function checks if leaf_qdisc->q.qlen > 0 to decide on deactivation, but this condition does not account for the scenario where multiple qfq_class objects share the same leaf_qdisc. The proper check should verify whether the aggregate is actually in an active state before attempting deactivation, rather than relying solely on the shared queue length metric.
Attack Vector
The vulnerability can be triggered through the netlink interface used for traffic control configuration. The call trace shows the attack path:
- An attacker sends a malicious netlink message to configure traffic filters via netlink_sendmsg()
- The message is processed through netlink_unicast() → netlink_rcv_skb() → rtnetlink_rcv_msg()
- The tc_new_tfilter() function processes the filter creation request
- During this process, qdisc_put() triggers __qdisc_destroy() → qdisc_reset() → qfq_reset_qdisc()
- The vulnerable qfq_deactivate_agg() function is called with an invalid aggregate pointer
This attack requires local access with privileges to configure traffic control (typically CAP_NET_ADMIN capability), though the crash trace shows the process name as "exploit", suggesting active exploitation research.
Detection Methods for CVE-2026-22976
Indicators of Compromise
- Kernel panic messages containing "BUG: kernel NULL pointer dereference" with address 0000000000000000
- Stack traces showing qfq_deactivate_agg → qfq_reset_qdisc → qdisc_reset call chain
- Oops messages referencing net/sched/sch_qfq.c with error code 0x0002 (supervisor write access)
- Unexpected system reboots on systems using QFQ scheduler with concurrent qdisc operations
Detection Strategies
- Monitor kernel logs for NULL pointer dereference errors in the sch_qfq module using dmesg or syslog
- Implement audit rules to track netlink traffic control operations: auditctl -a always,exit -F arch=b64 -S sendmsg -F scontext_type=netlink_route_socket
- Use crash dump analysis tools to identify stack traces matching the vulnerable code path
- Deploy eBPF-based monitoring on tc_new_tfilter() to detect anomalous filter creation patterns
Monitoring Recommendations
- Configure kdump or kexec to capture crash dumps for forensic analysis of potential exploitation
- Enable kernel crash reporting via kerneloops service to centralize and alert on kernel panic events
- Monitor for unusual traffic control configuration activity using tc monitor in audit mode
- Implement real-time log monitoring for kernel: messages containing qfq or sch_qfq references
How to Mitigate CVE-2026-22976
Immediate Actions Required
- Apply the kernel patches from the stable branches addressing this vulnerability
- If immediate patching is not possible, consider unloading the sch_qfq module if QFQ scheduling is not required: modprobe -r sch_qfq
- Restrict access to traffic control configuration by limiting CAP_NET_ADMIN capability to essential services only
- Monitor systems for unexpected crashes while preparing for kernel updates
Patch Information
Multiple patches have been released across Linux kernel stable branches to address this vulnerability. The fix modifies the aggregate deactivation logic in qfq_reset() to properly check whether an aggregate is active before attempting deactivation.
Available patch commits:
- Commit 0809c4bc06c9
- Commit 11bf9134613f
- Commit 43497313d0da
- Commit 51ffd447bc37
- Commit 6116a83ec167
- Commit c1d73b148023
- Commit cdb24200b043
Organizations should identify their kernel version and apply the appropriate patch from the corresponding stable branch.
Workarounds
- Blacklist the sch_qfq module to prevent automatic loading: echo "blacklist sch_qfq" >> /etc/modprobe.d/blacklist-qfq.conf
- Use alternative traffic schedulers such as fq_codel, htb, or sfq which are not affected by this vulnerability
- Implement network namespace isolation to limit the impact of potential exploitation
- Restrict netlink socket access using SELinux or AppArmor policies to minimize the attack surface
# Configuration example: Blacklist sch_qfq module
echo "blacklist sch_qfq" | sudo tee /etc/modprobe.d/blacklist-qfq.conf
# Verify module is not loaded
lsmod | grep sch_qfq
# Remove module if currently loaded (if not in active use)
sudo modprobe -r sch_qfq
# Update initramfs to persist changes
sudo update-initramfs -u
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

