Re: [PATCH net 1/2] net/sched: bound TCA_STAB overhead to prevent qdisc soft lockup
Jamal Hadi Salim <[email protected]>
| Newsgroups | org.kernel.vger.netdev,org.kernel.vger.stable |
|---|---|
| Message-ID | <CAM0EoMkoRBX5-RZu4-zwJ9CEDFj6sE7q5mzvsoS1bsbjYkz=4w@mail.gmail.com> |
On Tue, Aug 18, 2026 at 6:17 AM Jamal Hadi Salim <[email protected]> wrote: > > qdisc_get_stab() copies the TCA_STAB_BASE overhead from userspace > without bounding it. A huge overhead (e.g. 2147483000) combined with > a small DRR class quantum (1) makes qdisc_calculate_pkt_len() set a > ~2 GiB accounting length, so drr_dequeue() spins ~2 billion times > adding quantum=1 to the deficit under the qdisc spinlock with BH > disabled, producing a soft lockup / RCU stall. > > The same unbounded stab overhead also lets qdisc_pkt_len() overflow > the per-flow counters of other qdiscs (fq_codel, hhf, sfq), which > were recently clamped per-qdisc; bounding the overhead at the source > closes the root cause for all of them. > > Reject overhead > 65535 (matching QFQ_MAX_LMAX and the max single > skb length) in qdisc_get_stab(); legitimate L1 overheads are a few > dozen bytes at most. > > [email protected] provided PoCs for both DRR and ETS demonstrating the > soft lockup. > > Conditions to recreate the bug: > - require CONFIG_SOFTLOCKUP_DETECTOR=y > - Create a veth pair (or use lo) and attach a root qdisc with a > TCA_STAB overhead near INT_MAX, e.g.: > tc qdisc add dev veth0 root handle 1: \ > stab mtu 2048 tsize 0 overhead 2147483000 drr > (or ... ets bands 1 strict 0 quanta 1 priomap 0 0 ... for ETS). > - Add a class with a tiny quantum of 1: > tc class add dev veth0 parent 1: classid 1:1 drr quantum 1 > - Send one small packet (e.g. `ping` or a single UDP datagram) on the > device; the ~2 GiB accounting length makes the deficit loop spin > billions of times under the qdisc lock, tripping 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 > (Level 2). > > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") > Reported-by: [email protected] > Tested-by: Victor Nogueira <[email protected]> > Signed-off-by: Jamal Hadi Salim <[email protected]> I sent v2 and i noticed when i changed the subject it confused patchwork -- pw-bot: cr cheers, jamal > --- > include/net/pkt_sched.h | 1 + > net/sched/sch_api.c | 5 +++++ > 2 files changed, 6 insertions(+) > > diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h > index 18a419cd9d94..a4c1b7b0263d 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 STAB_OVERHEAD_MAX 65535 > > struct qdisc_walker { > int stop; > diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c > index 65b35528d125..cdcf861c85f5 100644 > --- a/net/sched/sch_api.c > +++ b/net/sched/sch_api.c > @@ -544,6 +544,11 @@ static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt, > return ERR_PTR(-EINVAL); > } > > + if (abs(s->overhead) > STAB_OVERHEAD_MAX) { > + NL_SET_ERR_MSG(extack, "Invalid size table overhead"); > + return ERR_PTR(-EINVAL); > + } > + > stab = kmalloc_flex(*stab, data, tsize); > if (!stab) > return ERR_PTR(-ENOMEM); > -- > 2.43.0 >