* [PATCH net] net/sched: taprio: fix infinite loop in taprio_dequeue_tc_priority()
@ 2026-09-11 17:18 李强
0 siblings, 0 replies; only message in thread
From: 李强 @ 2026-09-11 17:18 UTC (permalink / raw)
To: vinicius.gomes
Cc: jhs, jiri, davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel
Dear All,
I am writing this email to report an infinite loop bug in taprio_dequeue_tc_priority() while fuzzing the Linux Ethernet networking stack on embedded devices.
taprio_dequeue_tc_priority() contains a do-while loop that iterates over TXQs belonging to a traffic class using a persistent cursor (q->cur_txq[tc]). The loop termination condition compares the current cursor position with its initial value (first_txq).
However, taprio_next_tc_txq() reads the TC-to-TXQ mapping from dev->tc_to_txq[tc], which is a per-device shared field. When another qdisc (e.g., mqprio) is grafted onto the same device with a different queue mapping, dev->tc_to_txq[] is updated but taprio's private cur_txq[] retains the stale value from the old mapping.
This creates a situation where first_txq (from old mapping A) falls outside the new [offset, offset+count) range (from new mapping B). The wrap logic in taprio_next_tc_txq() confines the cursor to the new range, so it can never reach first_txq, causing an infinite loop
in softirq context with interrupts disabled.
The loop manifests as a hard CPU lockup: the affected core spins indefinitely in taprio_dequeue_from_txq(), timer ticks stop, RCU
stalls, and the rtnl_lock is held forever -- blocking all network configuration (tc, ip, netlink) and even reboot. Only SysRq hard
reset can recover the system.
This is 100% deterministic with just 2 tc commands and zero network traffic:
tc qdisc replace dev eth0 root taprio \
num_tc 3 map 0 0 1 1 2 2 0 0 0 0 0 0 0 0 0 0 \
queues 1@0 1@1 1@2 base-time 0 \
sched-entry S ff 1000000 clockid CLOCK_TAI
tc qdisc replace dev eth0 root mqprio \
num_tc 3 map 0 0 1 1 2 2 0 0 0 0 0 0 0 0 0 0 \
queues 1@1 1@2 1@3
The second command triggers the graft path: mqprio writes dev->tc_to_txq[] with the new mapping, then dev_deactivate() fires
a pending TX softirq that calls taprio's dequeue with the stale cursor -- instant deadloop.
Tested on:
- aarch64 (6.12.73-rt15+, stmmac physical NIC): 6.5h lockup
- x86_64 (6.8.0-138-generic, veth virtual NIC): instant lockup
Fix by clamping first_txq into the current [offset, offset+count) range read from dev->tc_to_txq[tc] at the start of each TC iteration. If the cursor is outside the valid range, reset it to offset. This adds a single READ_ONCE + branch per TC per dequeue call with negligible overhead.
Fixes: 2f530df76c8c ("net/sched: taprio: give higher priority to higher TCs in software dequeue mode")
Signed-off-by: LiQiang <liqiang35@xiaomi.com>
---
net/sched/sch_taprio.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 39ac5b97a..9ae0de007 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -781,6 +781,7 @@ static struct sk_buff *taprio_dequeue_tc_priority(struct Qdisc *sch,
struct taprio_sched *q = qdisc_priv(sch);
struct net_device *dev = qdisc_dev(sch);
int num_tc = netdev_get_num_tc(dev);
+ struct netdev_tc_txq tc_txq;
struct sk_buff *skb;
int tc;
@@ -790,6 +791,19 @@ static struct sk_buff *taprio_dequeue_tc_priority(struct Qdisc *sch,
if (!(gate_mask & BIT(tc)))
continue;
+ /* Clamp the persistent cursor into the current tc_to_txq
+ * range. Another qdisc (e.g. mqprio) grafted onto the same
+ * device may have rewritten dev->tc_to_txq[] since taprio
+ * last set cur_txq[], making first_txq fall outside the new
+ * [offset, offset+count) interval and the do-while exit
+ * condition unreachable -- an infinite loop.
+ */
+ tc_txq.combined = READ_ONCE(dev->tc_to_txq[tc].combined);
+ if (tc_txq.count == 0 ||
+ first_txq < tc_txq.offset ||
+ first_txq >= tc_txq.offset + tc_txq.count)
+ q->cur_txq[tc] = first_txq = tc_txq.offset;
+
do {
skb = taprio_dequeue_from_txq(sch, q->cur_txq[tc],
entry, gate_mask);
--
2.50.1
Best regards,
LiQiang
Xiaomi ShadowBlade Security Lab
#/******本邮件及其附件含有小米公司的保密信息,仅限于发送给上面地址中列出的个人或群组。禁止任何其他人以任何形式使用(包括但不限于全部或部分地泄露、复制、或散发)本邮件中的信息。如果您错收了本邮件,请您立即电话或邮件通知发件人并删除本邮件! This e-mail and its attachments contain confidential information from XIAOMI, which is intended only for the person or entity whose address is listed above. Any use of the information contained herein in any way (including, but not limited to, total or partial disclosure, reproduction, or dissemination) by persons other than the intended recipient(s) is prohibited. If you receive this e-mail in error, please notify the sender by phone or email immediately and delete it!******/#
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-11 17:18 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 17:18 [PATCH net] net/sched: taprio: fix infinite loop in taprio_dequeue_tc_priority() 李强
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®