netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Kuniyuki Iwashima <kuniyu@google.com>
To: Jamal Hadi Salim <jhs@mojatatu.com>,
	Cong Wang <xiyou.wangcong@gmail.com>,
	 Jiri Pirko <jiri@resnulli.us>,
	"David S. Miller" <davem@davemloft.net>,
	 Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>,
	Paolo Valente <paolo.valente@unimore.it>,
	 Kuniyuki Iwashima <kuniyu@google.com>,
	Kuniyuki Iwashima <kuni1840@gmail.com>,
	netdev@vger.kernel.org,
	 syzbot+ec7176504e5bcc33ca4e@syzkaller.appspotmail.com
Subject: [PATCH v1 net] net: sched: sch_qfq: Fix use-after-free in qfq_reset_qdisc().
Date: Thu,  6 Nov 2025 07:10:49 +0000	[thread overview]
Message-ID: <20251106071050.494080-1-kuniyu@google.com> (raw)

syzbot reported use-after-free in qfq_reset_qdisc() while
accessing struct qfq_class hashed in qfq_sched.clhash.hash[]. [0]

When qfq_find_agg() returns NULL in qfq_change_class(),
struct qfq_aggregate is allocated.

If it fails, qfq_class is kfree()d at the destroy_class: label.

However, if it happens when qfq_change_class() is called for an
existing class, just freeing it is not sufficient, leaking the old
qfq_aggregate and leaving qfq_class in qfq_sched.clhash.hash[].

Let's call qfq_delete_class() in such a case.

We need to move up qfq_destroy_class() and qfq_delete_class().

[0]:
BUG: KASAN: slab-use-after-free in qfq_reset_qdisc+0xcc/0x208 net/sched/sch_qfq.c:1484
Read of size 8 at addr ffff0000ca2bfe50 by task syz.0.17/6716
CPU: 0 UID: 0 PID: 6716 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 06/30/2025
Call trace:
 show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:499 (C)
 __dump_stack+0x30/0x40 lib/dump_stack.c:94
 dump_stack_lvl+0xd8/0x12c lib/dump_stack.c:120
 print_address_description+0xa8/0x238 mm/kasan/report.c:378
 print_report+0x68/0x84 mm/kasan/report.c:482
 kasan_report+0xb0/0x110 mm/kasan/report.c:595
 __asan_report_load8_noabort+0x20/0x2c mm/kasan/report_generic.c:381
 qfq_reset_qdisc+0xcc/0x208 net/sched/sch_qfq.c:1484
 qdisc_reset+0x128/0x598 net/sched/sch_generic.c:1038
 __qdisc_destroy+0x134/0x4bc net/sched/sch_generic.c:1077
 qdisc_put net/sched/sch_generic.c:1109 [inline]
 dev_shutdown+0x35c/0x47c net/sched/sch_generic.c:1497
 unregister_netdevice_many_notify+0xbb8/0x1de0 net/core/dev.c:12242
 unregister_netdevice_many net/core/dev.c:12317 [inline]
 unregister_netdevice_queue+0x2b4/0x300 net/core/dev.c:12161
 unregister_netdevice include/linux/netdevice.h:3389 [inline]
 __tun_detach+0x5d4/0x1304 drivers/net/tun.c:621
 tun_detach drivers/net/tun.c:637 [inline]
 tun_chr_close+0x118/0x1f8 drivers/net/tun.c:3436
 __fput+0x340/0x75c fs/file_table.c:468
 ____fput+0x20/0x58 fs/file_table.c:496
 task_work_run+0x1dc/0x260 kernel/task_work.c:227
 resume_user_mode_work include/linux/resume_user_mode.h:50 [inline]
 exit_to_user_mode_loop+0xfc/0x178 kernel/entry/common.c:43
 exit_to_user_mode_prepare include/linux/irq-entry-common.h:225 [inline]
 arm64_exit_to_user_mode arch/arm64/kernel/entry-common.c:103 [inline]
 el0_svc+0x170/0x254 arch/arm64/kernel/entry-common.c:747
 el0t_64_sync_handler+0x84/0x12c arch/arm64/kernel/entry-common.c:765
 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596

Allocated by task 6716:
 kasan_save_stack mm/kasan/common.c:56 [inline]
 kasan_save_track+0x40/0x78 mm/kasan/common.c:77
 kasan_save_alloc_info+0x44/0x54 mm/kasan/generic.c:573
 poison_kmalloc_redzone mm/kasan/common.c:400 [inline]
 __kasan_kmalloc+0x9c/0xb4 mm/kasan/common.c:417
 kasan_kmalloc include/linux/kasan.h:262 [inline]
 __kmalloc_cache_noprof+0x3a4/0x65c mm/slub.c:5748
 kmalloc_noprof include/linux/slab.h:957 [inline]
 kzalloc_noprof include/linux/slab.h:1094 [inline]
 qfq_change_class+0x498/0xbe8 net/sched/sch_qfq.c:479
 __tc_ctl_tclass net/sched/sch_api.c:2274 [inline]
 tc_ctl_tclass+0x988/0x10b0 net/sched/sch_api.c:2304
 rtnetlink_rcv_msg+0x624/0x97c net/core/rtnetlink.c:6963
 netlink_rcv_skb+0x220/0x3fc net/netlink/af_netlink.c:2552
 rtnetlink_rcv+0x28/0x38 net/core/rtnetlink.c:6981
 netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
 netlink_unicast+0x694/0x8c4 net/netlink/af_netlink.c:1346
 netlink_sendmsg+0x648/0x930 net/netlink/af_netlink.c:1896
 sock_sendmsg_nosec net/socket.c:727 [inline]
 __sock_sendmsg net/socket.c:742 [inline]
 ____sys_sendmsg+0x490/0x7b8 net/socket.c:2630
 ___sys_sendmsg+0x204/0x278 net/socket.c:2684
 __sys_sendmsg net/socket.c:2716 [inline]
 __do_sys_sendmsg net/socket.c:2721 [inline]
 __se_sys_sendmsg net/socket.c:2719 [inline]
 __arm64_sys_sendmsg+0x184/0x238 net/socket.c:2719
 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
 invoke_syscall+0x98/0x254 arch/arm64/kernel/syscall.c:49
 el0_svc_common+0x130/0x23c arch/arm64/kernel/syscall.c:132
 do_el0_svc+0x48/0x58 arch/arm64/kernel/syscall.c:151
 el0_svc+0x5c/0x254 arch/arm64/kernel/entry-common.c:746
 el0t_64_sync_handler+0x84/0x12c arch/arm64/kernel/entry-common.c:765
 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596

Freed by task 6716:
 kasan_save_stack mm/kasan/common.c:56 [inline]
 kasan_save_track+0x40/0x78 mm/kasan/common.c:77
 __kasan_save_free_info+0x58/0x70 mm/kasan/generic.c:587
 kasan_save_free_info mm/kasan/kasan.h:406 [inline]
 poison_slab_object mm/kasan/common.c:252 [inline]
 __kasan_slab_free+0x74/0xa4 mm/kasan/common.c:284
 kasan_slab_free include/linux/kasan.h:234 [inline]
 slab_free_hook mm/slub.c:2523 [inline]
 slab_free mm/slub.c:6611 [inline]
 kfree+0x184/0x600 mm/slub.c:6818
 qfq_change_class+0x92c/0xbe8 net/sched/sch_qfq.c:533
 __tc_ctl_tclass net/sched/sch_api.c:2274 [inline]
 tc_ctl_tclass+0x988/0x10b0 net/sched/sch_api.c:2304
 rtnetlink_rcv_msg+0x624/0x97c net/core/rtnetlink.c:6963
 netlink_rcv_skb+0x220/0x3fc net/netlink/af_netlink.c:2552
 rtnetlink_rcv+0x28/0x38 net/core/rtnetlink.c:6981
 netlink_unicast_kernel net/netlink/af_netlink.c:1320 [inline]
 netlink_unicast+0x694/0x8c4 net/netlink/af_netlink.c:1346
 netlink_sendmsg+0x648/0x930 net/netlink/af_netlink.c:1896
 sock_sendmsg_nosec net/socket.c:727 [inline]
 __sock_sendmsg net/socket.c:742 [inline]
 ____sys_sendmsg+0x490/0x7b8 net/socket.c:2630
 ___sys_sendmsg+0x204/0x278 net/socket.c:2684
 __sys_sendmsg net/socket.c:2716 [inline]
 __do_sys_sendmsg net/socket.c:2721 [inline]
 __se_sys_sendmsg net/socket.c:2719 [inline]
 __arm64_sys_sendmsg+0x184/0x238 net/socket.c:2719
 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
 invoke_syscall+0x98/0x254 arch/arm64/kernel/syscall.c:49
 el0_svc_common+0x130/0x23c arch/arm64/kernel/syscall.c:132
 do_el0_svc+0x48/0x58 arch/arm64/kernel/syscall.c:151
 el0_svc+0x5c/0x254 arch/arm64/kernel/entry-common.c:746
 el0t_64_sync_handler+0x84/0x12c arch/arm64/kernel/entry-common.c:765
 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:596

Fixes: 462dbc9101ac ("pkt_sched: QFQ Plus: fair-queueing service at DRR cost")
Reported-by: syzbot+ec7176504e5bcc33ca4e@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/690c48f2.050a0220.baf87.0080.GAE@google.com/
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/sched/sch_qfq.c | 68 +++++++++++++++++++++++++--------------------
 1 file changed, 38 insertions(+), 30 deletions(-)

diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 2255355e51d3..3d1522b1b88a 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -403,6 +403,36 @@ static int qfq_change_agg(struct Qdisc *sch, struct qfq_class *cl, u32 weight,
 	return 0;
 }
 
+static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
+{
+	gen_kill_estimator(&cl->rate_est);
+	qdisc_put(cl->qdisc);
+	kfree(cl);
+}
+
+static int qfq_delete_class(struct Qdisc *sch, unsigned long arg,
+			    struct netlink_ext_ack *extack)
+{
+	struct qfq_sched *q = qdisc_priv(sch);
+	struct qfq_class *cl = (struct qfq_class *)arg;
+
+	if (qdisc_class_in_use(&cl->common)) {
+		NL_SET_ERR_MSG_MOD(extack, "QFQ class in use");
+		return -EBUSY;
+	}
+
+	sch_tree_lock(sch);
+
+	qdisc_purge_queue(cl->qdisc);
+	qdisc_class_hash_remove(&q->clhash, &cl->common);
+	qfq_rm_from_agg(q, cl);
+
+	sch_tree_unlock(sch);
+
+	qfq_destroy_class(sch, cl);
+	return 0;
+}
+
 static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
 			    struct nlattr **tca, unsigned long *arg,
 			    struct netlink_ext_ack *extack)
@@ -511,6 +541,10 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
 		new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL);
 		if (new_agg == NULL) {
 			err = -ENOBUFS;
+
+			if (existing)
+				goto delete_class;
+
 			gen_kill_estimator(&cl->rate_est);
 			goto destroy_class;
 		}
@@ -528,40 +562,14 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
 	*arg = (unsigned long)cl;
 	return 0;
 
-destroy_class:
-	qdisc_put(cl->qdisc);
-	kfree(cl);
+delete_class:
+	qfq_delete_class(sch, (unsigned long)cl, extack);
 	return err;
-}
 
-static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
-{
-	gen_kill_estimator(&cl->rate_est);
+destroy_class:
 	qdisc_put(cl->qdisc);
 	kfree(cl);
-}
-
-static int qfq_delete_class(struct Qdisc *sch, unsigned long arg,
-			    struct netlink_ext_ack *extack)
-{
-	struct qfq_sched *q = qdisc_priv(sch);
-	struct qfq_class *cl = (struct qfq_class *)arg;
-
-	if (qdisc_class_in_use(&cl->common)) {
-		NL_SET_ERR_MSG_MOD(extack, "QFQ class in use");
-		return -EBUSY;
-	}
-
-	sch_tree_lock(sch);
-
-	qdisc_purge_queue(cl->qdisc);
-	qdisc_class_hash_remove(&q->clhash, &cl->common);
-	qfq_rm_from_agg(q, cl);
-
-	sch_tree_unlock(sch);
-
-	qfq_destroy_class(sch, cl);
-	return 0;
+	return err;
 }
 
 static unsigned long qfq_search_class(struct Qdisc *sch, u32 classid)
-- 
2.51.2.1026.g39e6a42477-goog


                 reply	other threads:[~2025-11-06  7:10 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251106071050.494080-1-kuniyu@google.com \
    --to=kuniyu@google.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=jhs@mojatatu.com \
    --cc=jiri@resnulli.us \
    --cc=kuba@kernel.org \
    --cc=kuni1840@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=paolo.valente@unimore.it \
    --cc=syzbot+ec7176504e5bcc33ca4e@syzkaller.appspotmail.com \
    --cc=xiyou.wangcong@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).