[BUG] general protection fault in qfq_qlen_notify
From: GangMin Kim <hidden>
Date: 2026-02-23 01:27:51
Also in:
lkml
Dear Linux kernel developers and maintainers, Using a modified version of syzkaller, I identified a new bug and refined the PoC, and the bug-related information is attached below.Please let me know if you need any further information. Summary A NULL Pointer Dereference occurs because there is no proper check for whether cl is NULL when a class is found via cops->find() and then used in cops->qlen_notify(). This description provides an explanation and KASAN report for qfq_qlen_notify, but the vulnerability can occur in various other places such as drr_qlen_notify, hfsc_qlen_notify, htb_qlen_notify, etc. Keywords - net/sched Kernel Info Version: (Output of /proc/version) - Linux version 7.0.0-rc1 Commit: (Git hash if applicable) - 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f Description(Root Cause)
void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
{
const struct Qdisc_class_ops *cops;
unsigned long cl;
u32 parentid;
bool notify;
int drops;
drops = max_t(int, n, 0);
rcu_read_lock();
while ((parentid = sch->parent)) {
if (parentid == TC_H_ROOT)
break;
if (sch->flags & TCQ_F_NOPARENT)
break;
/* Notify parent qdisc only if child qdisc becomes empty. */
notify = !sch->q.qlen;
/* TODO: perform the search on a per txq basis */
sch = qdisc_lookup_rcu(qdisc_dev(sch), TC_H_MAJ(parentid));
if (sch == NULL) {
WARN_ON_ONCE(parentid != TC_H_ROOT);
break;
}
cops = sch->ops->cl_ops;
if (notify && cops->qlen_notify) { // [1]
/* Note that qlen_notify must be idempotent as it may get called
* multiple times.
*/
cl = cops->find(sch, parentid); // [2]
cops->qlen_notify(sch, cl); // [3]
}
sch->q.qlen -= n;
sch->qstats.backlog -= len;
__qdisc_qstats_drop(sch, drops);
}
rcu_read_unlock();
}
When condition [1] is satisfied, cl is retrieved at [2] and then passed to [3]. Here, [2] can return NULL, and using this value at [3] can cause a NULL Pointer Dereference.
static unsigned long qfq_search_class(struct Qdisc *sch, u32 classid)
{
return (unsigned long)qfq_find_class(sch, classid);
}
static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
{
struct qfq_sched *q = qdisc_priv(sch);
struct Qdisc_class_common *clc;
clc = qdisc_class_find(&q->clhash, classid);
if (clc == NULL)
return NULL; // [4]
return container_of(clc, struct qfq_class, common);
}
When QFQ is configured, the above function is executed at [2], and NULL can be returned at [4].
static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg)
{
struct qfq_sched *q = qdisc_priv(sch);
struct qfq_class *cl = (struct qfq_class *)arg; // [5]
if (list_empty(&cl->alist)) // [6]
return;
qfq_deactivate_class(q, cl);
}
If NULL is returned at [2], that value is passed directly to [3], which becomes cl through [5]. Subsequently, a NULL Pointer Dereference occurs at [6]. Kasan Report Oops: general protection fault, probably for non-canonical address 0xdffffc000000000b: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000058-0x000000000000005f] CPU: 0 UID: 0 PID: 377 Comm: test Not tainted 7.0.0-rc1 #54 PREEMPT(full) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 RIP: 0010:qfq_qlen_notify+0x2e/0x180 net/sched/sch_qfq.c:1434 Code: 41 57 41 56 41 55 41 54 55 48 89 fd 53 48 89 f3 4c 8d 7b 58 e8 e3 b3 8a fd 4c 89 fa 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 <80> 3c 02 00 0f 85 f8 00 00 00 48 8b 43 58 49 39 c7 0f 84 b9 00 00 RSP: 0018:ffff88811a447258 EFLAGS: 00010212 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffffffff83e2af6d RDX: 000000000000000b RSI: 0000000000000000 RDI: ffff88811b524000 RBP: ffff88811b524000 R08: 0000000000000000 R09: fffffbfff0d3595c R10: ffffffff869acae7 R11: 00000000ac3123a9 R12: ffffffff859b0b60 R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058 FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0 PKRU: 55555554 Call Trace: <TASK> __qdisc_qstats_drop include/net/sch_generic.h:958 [inline] qdisc_tree_reduce_backlog+0x128/0x400 net/sched/sch_api.c:812 tbf_offload_graft net/sched/sch_tbf.c:191 [inline] tbf_graft+0x1cc/0x590 net/sched/sch_tbf.c:573 qdisc_refcount_dec_if_one include/net/sch_generic.h:152 [inline] qdisc_graft+0x2ef/0x1460 net/sched/sch_api.c:1117 __tc_modify_qdisc net/sched/sch_api.c:1631 [inline] tc_modify_qdisc+0xf4f/0x1e40 net/sched/sch_api.c:1819 rcu_read_unlock include/linux/rcupdate.h:883 [inline] rtnetlink_rcv_msg+0x3b9/0xa90 net/core/rtnetlink.c:6913 netlink_rcv_skb+0x12e/0x390 net/netlink/af_netlink.c:2539 kfree_skb_reason include/linux/skbuff.h:1322 [inline] kfree_skb include/linux/skbuff.h:1331 [inline] netlink_unicast_kernel net/netlink/af_netlink.c:1321 [inline] netlink_unicast+0x6c1/0x970 net/netlink/af_netlink.c:1344 netlink_sendmsg+0x79c/0xc50 net/netlink/af_netlink.c:2465 __sock_release net/socket.c:673 [inline] ____sys_sendmsg+0x8b2/0xa50 net/socket.c:690 sendmsg_copy_msghdr net/socket.c:2621 [inline] ___sys_sendmsg+0x120/0x1c0 net/socket.c:2642 __sys_sendmsg+0x147/0x1f0 net/socket.c:2681 arch_atomic64_read arch/x86/include/asm/atomic64_64.h:15 [inline] raw_atomic64_read include/linux/atomic/atomic-arch-fallback.h:2583 [inline] raw_atomic_long_read include/linux/atomic/atomic-long.h:38 [inline] atomic_long_read include/linux/atomic/atomic-instrumented.h:3189 [inline] unwind_reset_info include/linux/unwind_deferred.h:37 [inline] exit_to_user_mode include/linux/irq-entry-common.h:296 [inline] syscall_exit_to_user_mode include/linux/entry-common.h:327 [inline] do_syscall_64+0xf1/0x530 arch/x86/entry/syscall_64.c:100 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7ff24e7c4e4d Code: 28 89 54 24 1c 48 89 74 24 10 89 7c 24 08 e8 ca ee ff ff 8b 54 24 1c 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 33 44 89 c7 48 89 44 24 08 e8 fe ee ff ff 48 RSP: 002b:00007ff24e5d9d90 EFLAGS: 00000293 ORIG_RAX: 000000000000002e RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff24e7c4e4d RDX: 0000000000000000 RSI: 00007ff24e5d9dd0 RDI: 0000000000000003 RBP: 00007ff24e5d9e30 R08: 0000000000000000 R09: 0000000000050000 R10: 0000000000000000 R11: 0000000000000293 R12: 00007ffcdc50ddde R13: 00007ffcdc50dddf R14: 00007ff24e5d9fc0 R15: 0000000000022000 </TASK> Modules linked in: ---[ end trace 0000000000000000 ]--- RIP: 0010:qfq_qlen_notify+0x2e/0x180 net/sched/sch_qfq.c:1434 Code: 41 57 41 56 41 55 41 54 55 48 89 fd 53 48 89 f3 4c 8d 7b 58 e8 e3 b3 8a fd 4c 89 fa 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 <80> 3c 02 00 0f 85 f8 00 00 00 48 8b 43 58 49 39 c7 0f 84 b9 00 00 RSP: 0018:ffff88811a447258 EFLAGS: 00010212 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffffffff83e2af6d RDX: 000000000000000b RSI: 0000000000000000 RDI: ffff88811b524000 RBP: ffff88811b524000 R08: 0000000000000000 R09: fffffbfff0d3595c R10: ffffffff869acae7 R11: 00000000ac3123a9 R12: ffffffff859b0b60 R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058 FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0 PKRU: 55555554 ---------------- Code disassembly (best guess): 0: 41 57 push %r15 2: 41 56 push %r14 4: 41 55 push %r13 6: 41 54 push %r12 8: 55 push %rbp 9: 48 89 fd mov %rdi,%rbp c: 53 push %rbx d: 48 89 f3 mov %rsi,%rbx 10: 4c 8d 7b 58 lea 0x58(%rbx),%r15 14: e8 e3 b3 8a fd call 0xfd8ab3fc 19: 4c 89 fa mov %r15,%rdx 1c: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax 23: fc ff df 26: 48 c1 ea 03 shr $0x3,%rdx * 2a: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping instruction 2e: 0f 85 f8 00 00 00 jne 0x12c 34: 48 8b 43 58 mov 0x58(%rbx),%rax 38: 49 39 c7 cmp %rax,%r15 3b: 0f .byte 0xf 3c: 84 .byte 0x84 3d: b9 .byte 0xb9 R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058 FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0 PKRU: 55555554 Kernel panic - not syncing: Fatal exception in interrupt Kernel Offset: disabled ---[ end Kernel panic - not syncing: Fatal exception in interrupt ]---