From: "Kito Xu (veritas501)" <hxzene@gmail.com>
To: Jamal Hadi Salim <jhs@mojatatu.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>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
"Kito Xu (veritas501)" <hxzene@gmail.com>
Subject: [PATCH] net: sched: teql: fix use-after-free in teql_master_xmit
Date: Mon, 13 Apr 2026 17:44:47 +0800 [thread overview]
Message-ID: <20260413094448.2263828-1-hxzene@gmail.com> (raw)
teql_destroy() traverses the circular slave list to unlink a slave
qdisc. It reads master->slaves as both the starting point and the
do-while termination sentinel. However, the data-path writers
teql_dequeue() and teql_master_xmit() concurrently modify
master->slaves without holding RTNL, running in softirq or process
context respectively. If master->slaves is overwritten to an
already-visited node mid-traversal, the loop exits early without
finding the target slave. The slave is never unlinked, but its memory
is freed by call_rcu() -- leaving a dangling pointer in the circular
list. The next teql_master_xmit() traversal dereferences freed memory.
race condition:
CPU 0 (teql_destroy, RTNL held) CPU 1 (teql_dequeue, softirq)
----------------------------------- -----------------------------
prev = master->slaves; // = A
q = NEXT_SLAVE(A); // = B
B == A? No.
prev = B;
/* slave C's queue drains */
skb == NULL ->
dat->m->slaves = C; /* write! */
q = NEXT_SLAVE(B); // = C
C == A? No.
prev = C;
/* check: (prev=C) != master->slaves(C)?
FALSE -> loop exits! */
/* A never unlinked, freed by call_rcu */
CPU 0 (teql_master_xmit, later)
-----------------------------------
q = NEXT_SLAVE(C); // = A (freed!)
slave = qdisc_dev(A); // UAF!
Fix this by saving master->slaves into a local `head` variable at the
start of teql_destroy() and using it as a stable sentinel for the
entire traversal. Also annotate all data-path accesses to
master->slaves with READ_ONCE/WRITE_ONCE to prevent store-tearing and
compiler-introduced re-reads.
==================================================================
BUG: KASAN: slab-use-after-free in teql_master_xmit+0xeae/0x14a0
Read of size 8 at addr ffff888018074040 by task poc/162
CPU: 2 UID: 0 PID: 162 Comm: poc Not tainted 7.0.0-rc7-next-20260410 #10 PREEMPTLAZY
Call Trace:
<TASK>
dump_stack_lvl+0x64/0x80
print_report+0xd0/0x5e0
kasan_report+0xce/0x100
teql_master_xmit+0xeae/0x14a0
dev_hard_start_xmit+0xcd/0x5b0
sch_direct_xmit+0x12e/0xac0
__qdisc_run+0x3b1/0x1a70
__dev_queue_xmit+0x2257/0x3100
ip_finish_output2+0x615/0x19c0
ip_output+0x158/0x2b0
ip_send_skb+0x11b/0x160
udp_send_skb+0x64b/0xd80
udp_sendmsg+0x138c/0x1ec0
__sys_sendto+0x331/0x3a0
__x64_sys_sendto+0xe0/0x1c0
do_syscall_64+0x64/0x680
entry_SYSCALL_64_after_hwframe+0x76/0x7e
The buggy address belongs to the object at ffff888018074000
which belongs to the cache kmalloc-512 of size 512
The buggy address is located 64 bytes inside of
freed 512-byte region [ffff888018074000, ffff888018074200)
==================================================================
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Kito Xu (veritas501) <hxzene@gmail.com>
---
net/sched/sch_teql.c | 24 ++++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c
index ec4039a201a2..2e86397a5219 100644
--- a/net/sched/sch_teql.c
+++ b/net/sched/sch_teql.c
@@ -101,7 +101,7 @@ teql_dequeue(struct Qdisc *sch)
if (skb == NULL) {
struct net_device *m = qdisc_dev(q);
if (m) {
- dat->m->slaves = sch;
+ WRITE_ONCE(dat->m->slaves, sch);
netif_wake_queue(m);
}
} else {
@@ -136,19 +136,23 @@ teql_destroy(struct Qdisc *sch)
if (!master)
return;
- prev = master->slaves;
+ prev = READ_ONCE(master->slaves);
if (prev) {
+ struct Qdisc *head = prev;
+
do {
q = NEXT_SLAVE(prev);
if (q == sch) {
NEXT_SLAVE(prev) = NEXT_SLAVE(q);
- if (q == master->slaves) {
- master->slaves = NEXT_SLAVE(q);
- if (q == master->slaves) {
+ if (q == head) {
+ WRITE_ONCE(master->slaves,
+ NEXT_SLAVE(q));
+ if (q == NEXT_SLAVE(q)) {
struct netdev_queue *txq;
txq = netdev_get_tx_queue(master->dev, 0);
- master->slaves = NULL;
+ WRITE_ONCE(master->slaves,
+ NULL);
dev_reset_queue(master->dev,
txq, NULL);
@@ -158,7 +162,7 @@ teql_destroy(struct Qdisc *sch)
break;
}
- } while ((prev = q) != master->slaves);
+ } while ((prev = q) != head);
}
}
@@ -285,7 +289,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
int subq = skb_get_queue_mapping(skb);
struct sk_buff *skb_res = NULL;
- start = master->slaves;
+ start = READ_ONCE(master->slaves);
restart:
nores = 0;
@@ -317,7 +321,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
netdev_start_xmit(skb, slave, slave_txq, false) ==
NETDEV_TX_OK) {
__netif_tx_unlock(slave_txq);
- master->slaves = NEXT_SLAVE(q);
+ WRITE_ONCE(master->slaves, NEXT_SLAVE(q));
netif_wake_queue(dev);
master->tx_packets++;
master->tx_bytes += length;
@@ -329,7 +333,7 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev)
busy = 1;
break;
case 1:
- master->slaves = NEXT_SLAVE(q);
+ WRITE_ONCE(master->slaves, NEXT_SLAVE(q));
return NETDEV_TX_OK;
default:
nores = 1;
--
2.43.0
next reply other threads:[~2026-04-13 9:44 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-13 9:44 Kito Xu (veritas501) [this message]
2026-04-15 15:31 ` [PATCH] net: sched: teql: fix use-after-free in teql_master_xmit Simon Horman
2026-04-16 15:43 ` Jamal Hadi Salim
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=20260413094448.2263828-1-hxzene@gmail.com \
--to=hxzene@gmail.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=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.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