qdisc_get_stab() accepts a user-supplied size table, and __qdisc_calculate_pkt_len() amplifies qdisc_pkt_len() through the overhead, the size-table data (u16), and size_log (up to STAB_SIZE_LOG_MAX). A crafted stab can therefore set qdisc_pkt_len() to ~1 GiB for an ordinary skb. Per-flow deficit schedulers such as DRR and ETS replenish one quantum per loop iteration; with a tiny quantum (1) they spin billions of times under the qdisc lock, producing a soft lockup / RCU stall. Cap the final qdisc_pkt_len() to GSO_MAX_SIZE so the size-table amplification cannot drive deficit schedulers into an unbounded loop. A legitimate size table (e.g. qfq's overhead 999999999, which is handled by dropping) is still accepted. Conditions to recreate the bug: - CONFIG_NET_SCHED=y, CONFIG_NET_SCH_DRR=y (or CONFIG_NET_SCH_ETS=y). - Attach a DRR (or ETS) root qdisc with a crafted TCA_STAB that amplifies qdisc_pkt_len to ~1 GiB (e.g. size_log=15, data=[32768]). - Add a class with a tiny quantum of 1 and send one small packet; the deficit loop spins billions of times under the qdisc lock and trips the softlockup detector (panic with kernel.softlockup_panic=1). - Reachable as root or from an unprivileged user in a fresh user+net namespace (unshare -Urn) with namespace-local CAP_NET_ADMIN. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: vega@nebusec.ai Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim --- v1 -> v2 1. Sigh. The v1 overhead cap broke the existing qfq tdc test 5993, caught by running the whole tdc.sh instead of affected qdiscs reported reported by poc. That test legitimately uses stab overhead 999999999 qfq and expects the qdisc to be accepted (exit 0) with packets dropped. 2. Better Fix: cap the final qdisc_pkt_len() to GSO_MAX_SIZE(524280) in __qdisc_calculate_pkt_len() per sashikos[1][2] suggestions 3. Given existence of tdc 5993 we dont need the tdc test created earlier since the essence of that test is covered in tdc 5993. [1] https://sashiko.dev/#/patchset/20260818101735.16655-1-jhs@mojatatu.com [2] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260818101735.16655-1-jhs@mojatatu.com --- net/sched/sch_api.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 65b35528d125..ad4f117ff55a 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -610,8 +610,11 @@ void __qdisc_calculate_pkt_len(struct sk_buff *skb, pkt_len <<= stab->szopts.size_log; out: - if (unlikely(pkt_len < 1)) - pkt_len = 1; + /* A size table can inflate qdisc_pkt_len() beyond any real packet + * (via overhead, the data table, or size_log); cap it so deficit + * schedulers such as DRR/ETS terminate their refill loops. + */ + pkt_len = clamp_t(int, pkt_len, 1, GSO_MAX_SIZE); qdisc_skb_cb(skb)->pkt_len = pkt_len; } -- 2.43.0