Commit 8f735d64382d for kernel

commit 8f735d64382dcf162f4276d6699d03ad2f859c0b
Author: Jamal Hadi Salim <jhs@mojatatu.com>
Date:   Tue Aug 25 04:14:03 2026 -0400

    net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup

    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 as illustrated by vega@nebusec.ai.

    Cap the final qdisc_pkt_len() to QDISC_PKT_LEN_MAX 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.

    Introduce cap QDISC_PKT_LEN_MAX (1 << 20) = 1 MiB which is well above
    any legitimate single-skb wire length: the largest current skb->len
    is GSO_MAX_SIZE (524280), and an ATM-style size table (53/48 cell tax)
    amplifies that to ~578 KB, both comfortably below 1 MiB. At the same
    time, 1 MiB bounds the deficit refill loop to ~1M iterations per
    packet with quantum=1, which completes in a few milliseconds well
    under the demonstrated softlockup threshold (~10^9 iterations).

    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 <victor@mojatatu.com>
    Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
    Link: https://patch.msgid.link/20260825081403.133992-1-jhs@mojatatu.com
    Signed-off-by: Jakub Kicinski <kuba@kernel.org>

diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
index 18a419cd9d94..90d3e7943b19 100644
--- a/include/net/pkt_sched.h
+++ b/include/net/pkt_sched.h
@@ -12,6 +12,7 @@

 #define DEFAULT_TX_QUEUE_LEN	1000
 #define STAB_SIZE_LOG_MAX	30
+#define QDISC_PKT_LEN_MAX	(1 << 20)	/* 1 MiB */

 struct qdisc_walker {
 	int	stop;
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 65b35528d125..90503e59e6e3 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, QDISC_PKT_LEN_MAX);
 	qdisc_skb_cb(skb)->pkt_len = pkt_len;
 }