Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
@ 2026-08-19 14:32 Jamal Hadi Salim
  2026-08-19 14:52 ` Eric Dumazet
  0 siblings, 1 reply; 10+ messages in thread
From: Jamal Hadi Salim @ 2026-08-19 14:32 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, stable, vega,
	Victor Nogueira

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 <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
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


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-19 14:32 [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup Jamal Hadi Salim
@ 2026-08-19 14:52 ` Eric Dumazet
  2026-08-19 14:58   ` Jamal Hadi Salim
  0 siblings, 1 reply; 10+ messages in thread
From: Eric Dumazet @ 2026-08-19 14:52 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, Jiri Pirko, David S. Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, stable, vega, Victor Nogueira

On Wed, Aug 19, 2026 at 4:33 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
>
> 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 <victor@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> ---
> 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);

Yeah, although it is a bit strange to mention GSO_MAX_SIZE in a function which
does not take care of GSO packets.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-19 14:52 ` Eric Dumazet
@ 2026-08-19 14:58   ` Jamal Hadi Salim
  2026-08-19 15:15     ` Eric Dumazet
  0 siblings, 1 reply; 10+ messages in thread
From: Jamal Hadi Salim @ 2026-08-19 14:58 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, Jiri Pirko, David S. Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, stable, vega, Victor Nogueira

On Wed, Aug 19, 2026 at 10:52 AM Eric Dumazet <edumazet@google.com> wrote:
>
> On Wed, Aug 19, 2026 at 4:33 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> >
> > 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 <victor@mojatatu.com>
> > Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> > ---
> > 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);
>
> Yeah, although it is a bit strange to mention GSO_MAX_SIZE in a function which
> does not take care of GSO packets.

well... there are many many references to gso under net/sched. What
dont you like about this specific things?

cheers,
jamal

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-19 14:58   ` Jamal Hadi Salim
@ 2026-08-19 15:15     ` Eric Dumazet
  2026-08-19 15:30       ` Jamal Hadi Salim
  0 siblings, 1 reply; 10+ messages in thread
From: Eric Dumazet @ 2026-08-19 15:15 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, Jiri Pirko, David S. Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, stable, vega, Victor Nogueira

On Wed, Aug 19, 2026 at 4:58 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
>
> On Wed, Aug 19, 2026 at 10:52 AM Eric Dumazet <edumazet@google.com> wrote:
> >
> > On Wed, Aug 19, 2026 at 4:33 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> > >
> > > 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 <victor@mojatatu.com>
> > > Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> > > ---
> > > 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);
> >
> > Yeah, although it is a bit strange to mention GSO_MAX_SIZE in a function which
> > does not take care of GSO packets.
>
> well... there are many many references to gso under net/sched. What
> dont you like about this specific things?

I just mentioned that __qdisc_calculate_pkt_len() never was updated to
deal with GSO packets.

It assumes packets with a single set of headers.

This is fine, I am sure nobody uses this stuff anymore.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-19 15:15     ` Eric Dumazet
@ 2026-08-19 15:30       ` Jamal Hadi Salim
  2026-08-24 18:36         ` Jakub Kicinski
  0 siblings, 1 reply; 10+ messages in thread
From: Jamal Hadi Salim @ 2026-08-19 15:30 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: netdev, Jiri Pirko, David S. Miller, Jakub Kicinski, Paolo Abeni,
	Simon Horman, stable, vega, Victor Nogueira

On Wed, Aug 19, 2026 at 11:15 AM Eric Dumazet <edumazet@google.com> wrote:
>
> On Wed, Aug 19, 2026 at 4:58 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> >
> > On Wed, Aug 19, 2026 at 10:52 AM Eric Dumazet <edumazet@google.com> wrote:
> > >
> > > On Wed, Aug 19, 2026 at 4:33 PM Jamal Hadi Salim <jhs@mojatatu.com> wrote:
> > > >
> > > > 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 <victor@mojatatu.com>
> > > > Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> > > > ---
> > > > 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);
> > >
> > > Yeah, although it is a bit strange to mention GSO_MAX_SIZE in a function which
> > > does not take care of GSO packets.
> >
> > well... there are many many references to gso under net/sched. What
> > dont you like about this specific things?
>
> I just mentioned that __qdisc_calculate_pkt_len() never was updated to
> deal with GSO packets.
>
> It assumes packets with a single set of headers.
>

I see. I was looking for something reasonable. The problem is maximum
real skb->len is UINT_MAX and with stab amplification it can get a
small packet into a GB.
Would you prefer a new #define in pkt_sched.h to put an upper bound
(QDISC_PKT_LEN_MAX)?
Something else?

cheers,
jamal


> This is fine, I am sure nobody uses this stuff anymore.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-19 15:30       ` Jamal Hadi Salim
@ 2026-08-24 18:36         ` Jakub Kicinski
  0 siblings, 0 replies; 10+ messages in thread
From: Jakub Kicinski @ 2026-08-24 18:36 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: Eric Dumazet, netdev, Jiri Pirko, David S. Miller, Paolo Abeni,
	Simon Horman, stable, vega, Victor Nogueira

On Wed, 19 Aug 2026 11:30:44 -0400 Jamal Hadi Salim wrote:
> I see. I was looking for something reasonable. The problem is maximum
> real skb->len is UINT_MAX and with stab amplification it can get a
> small packet into a GB.
> Would you prefer a new #define in pkt_sched.h to put an upper bound
> (QDISC_PKT_LEN_MAX)?
> Something else?

Let's go with the define you mentioned to make progress.
-- 
pw-bot: cr

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
@ 2026-08-25  8:14 Jamal Hadi Salim
  2026-08-27 11:01 ` Paolo Abeni
  2026-08-27 19:40 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 10+ messages in thread
From: Jamal Hadi Salim @ 2026-08-25  8:14 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, stable, vega,
	Victor Nogueira

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>
---
v1 -> v2:
- Use a dedicated QDISC_PKT_LEN_MAX define in include/net/pkt_sched.h
  instead of GSO_MAX_SIZE (Jakub Kicinski, Eric Dumazet).
  __qdisc_calculate_pkt_len() deals with single-skb wire-length
  accounting, not GSO segments; GSO_MAX_SIZE is semantically wrong.
  QDISC_PKT_LEN_MAX = (1 << 20) = 1 MiB avoids truncating legitimate
  stab accounting (BIG TCP + ATM-style 53/48 table ~ 578 KB) while
  still bounding the deficit loop to ~1M iterations at quantum=1
  (completes in a few ms, well under the softlockup threshold of
  ~10^9 iterations).
---
 include/net/pkt_sched.h | 1 +
 net/sched/sch_api.c     | 7 +++++--
 2 files changed, 6 insertions(+), 2 deletions(-)

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;
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-25  8:14 Jamal Hadi Salim
@ 2026-08-27 11:01 ` Paolo Abeni
  2026-08-27 17:48   ` Jamal Hadi Salim
  2026-08-27 19:40 ` patchwork-bot+netdevbpf
  1 sibling, 1 reply; 10+ messages in thread
From: Paolo Abeni @ 2026-08-27 11:01 UTC (permalink / raw)
  To: jhs
  Cc: netdev, jiri, davem, edumazet, kuba, pabeni, horms, stable, vega,
	victor

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup

This adds QDISC_PKT_LEN_MAX (1 MiB) and clamps the value computed by
__qdisc_calculate_pkt_len() into the range [1, QDISC_PKT_LEN_MAX], with the
stated goal of preventing a crafted TCA_STAB size table from driving the DRR
and ETS deficit refill loops into a multi-billion iteration spin under the
qdisc lock.

A couple of questions about the fix and about the arithmetic it now
consumes.

> diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
> index 18a419cd9d949..90d3e7943b19a 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 65b35528d125f..90503e59e6e3b 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;

[Severity: Medium]
This isn't a bug introduced by this patch, but the new clamp consumes the
result of arithmetic that is done entirely in a signed int on unvalidated
user input, so it may not bound what it is intended to bound.

In __qdisc_calculate_pkt_len():

	pkt_len = skb->len + stab->szopts.overhead;
	...
	pkt_len = stab->data[stab->szopts.tsize - 1] *
			(slot / stab->szopts.tsize) +
			stab->data[slot % stab->szopts.tsize];

	pkt_len <<= stab->szopts.size_log;

qdisc_get_stab() only validates tsize consistency and size_log/cell_log
against STAB_SIZE_LOG_MAX:

	if (s->size_log > STAB_SIZE_LOG_MAX ||
	    s->cell_log > STAB_SIZE_LOG_MAX) {
		NL_SET_ERR_MSG(extack, "Invalid logarithmic size of size table");
		return ERR_PTR(-EINVAL);
	}

The overhead field and the u16 data table are not range checked (the
changelog itself notes overhead 999999999 is accepted), and size_log may be
up to 30, so the shift overflows for any table entry >= 4.

Is the signed left shift here reportable under CONFIG_UBSAN_SHIFT, and can
that be reached by a namespace-local CAP_NET_ADMIN user?

With the usual wrapping behaviour, is the new clamp monotone in the
attacker-controlled inputs? For example data[slot] = 65535 with
size_log = 16 wraps to -65536, which clamp_t(int, ...) turns into 1, so
shaping qdiscs such as TBF, HTB and fq would account one byte for a packet
the table describes as huge.

Would doing this arithmetic in u64 (or unsigned with saturation) and
narrowing before the clamp make the new bound actually hold?

>  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;
>  }

[Severity: High]
Does this clamp actually bound the DRR/ETS refill loops, or only one of
their inputs?

drr_dequeue() round-robins the active class list, and each unsuccessful
visit advances only the head class by one quantum:

net/sched/sch_drr.c:drr_dequeue() {
	while (1) {
		cl = list_first_entry(&q->active, struct drr_class, alist);
		skb = cl->qdisc->ops->peek(cl->qdisc);
		...
		len = qdisc_pkt_len(skb);
		if (len <= cl->deficit) {
			...
			return skb;
		}

		WRITE_ONCE(cl->deficit, cl->deficit + READ_ONCE(cl->quantum));
		list_move_tail(&cl->alist, &q->active);
	}
}

Since every active class advances at the same rate, isn't the iteration
count before the first packet becomes eligible roughly
nr_active_classes * (qdisc_pkt_len / quantum) rather than
qdisc_pkt_len / quantum?

The minimum accepted quantum is 1, since drr_change_class() only rejects
zero:

net/sched/sch_drr.c:drr_change_class() {
	quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
	if (quantum == 0) {
		NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero");
		return -EINVAL;
	}
}

and drr_enqueue() re-arms the worst case on every reactivation:

net/sched/sch_drr.c:drr_enqueue() {
	if (!cl_is_active(cl)) {
		list_add_tail(&cl->alist, &q->active);
		WRITE_ONCE(cl->deficit, READ_ONCE(cl->quantum));
	}
}

So with quantum 1, the new 2^20 cap and roughly 1024 active classes (class
creation is not bounded, and drr_classify() maps skb->priority to a classid
so an unprivileged socket can steer packets per class), does a single
drr_dequeue() still run on the order of 10^9 iterations with the root qdisc
spinlock held and softirqs disabled?

The same shape appears in ets_qdisc_dequeue(), which is the other consumer
named in the new comment:

net/sched/sch_ets.c:ets_qdisc_dequeue() {
		cl->deficit += READ_ONCE(cl->quantum);
		list_move_tail(&cl->alist, &q->active);
}

with an added per-iteration scan of the q->nstrict strict bands.

Also, is this reachable with no TCA_STAB at all, for instance ~16k classes
with quantum 1 and ordinary 64 KiB packets? If so, the deficit-loop stall
does not appear to be gated on size-table amplification in the first place.

And can __qdisc_run() interrupt it?

net/sched/sch_generic.c:__qdisc_run() {
	while (qdisc_restart(q, &packets, quota)) {
		quota -= packets;
		if (quota <= 0) {
			...
			break;
		}
	}
}

The quota is only evaluated after qdisc_restart() returns, so it cannot
preempt the internal refill loop.

Would bounding the loop itself, either with an iteration limit or by
requiring a quantum that is sane relative to the maximum pkt_len, be
needed instead of clamping one of its inputs?
-- 
This is an AI-generated review.


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-27 11:01 ` Paolo Abeni
@ 2026-08-27 17:48   ` Jamal Hadi Salim
  0 siblings, 0 replies; 10+ messages in thread
From: Jamal Hadi Salim @ 2026-08-27 17:48 UTC (permalink / raw)
  To: Paolo Abeni
  Cc: netdev, jiri, davem, edumazet, kuba, horms, stable, vega, victor

On Thu, Aug 27, 2026 at 7:01 AM Paolo Abeni <pabeni@redhat.com> wrote:
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://netdev-ai.bots.linux.dev/sashiko/

I have two followups both targetted for net-next. Both are valid but
pre-existing different-root-cause bugs, not things this patch
introduces or should fold in tbh.

cheers,
jamal

> ---
> net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
>
> This adds QDISC_PKT_LEN_MAX (1 MiB) and clamps the value computed by
> __qdisc_calculate_pkt_len() into the range [1, QDISC_PKT_LEN_MAX], with the
> stated goal of preventing a crafted TCA_STAB size table from driving the DRR
> and ETS deficit refill loops into a multi-billion iteration spin under the
> qdisc lock.
>
> A couple of questions about the fix and about the arithmetic it now
> consumes.
>
> > diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h
> > index 18a419cd9d949..90d3e7943b19a 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 65b35528d125f..90503e59e6e3b 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;
>
> [Severity: Medium]
> This isn't a bug introduced by this patch, but the new clamp consumes the
> result of arithmetic that is done entirely in a signed int on unvalidated
> user input, so it may not bound what it is intended to bound.
>
> In __qdisc_calculate_pkt_len():
>
>         pkt_len = skb->len + stab->szopts.overhead;
>         ...
>         pkt_len = stab->data[stab->szopts.tsize - 1] *
>                         (slot / stab->szopts.tsize) +
>                         stab->data[slot % stab->szopts.tsize];
>
>         pkt_len <<= stab->szopts.size_log;
>
> qdisc_get_stab() only validates tsize consistency and size_log/cell_log
> against STAB_SIZE_LOG_MAX:
>
>         if (s->size_log > STAB_SIZE_LOG_MAX ||
>             s->cell_log > STAB_SIZE_LOG_MAX) {
>                 NL_SET_ERR_MSG(extack, "Invalid logarithmic size of size table");
>                 return ERR_PTR(-EINVAL);
>         }
>
> The overhead field and the u16 data table are not range checked (the
> changelog itself notes overhead 999999999 is accepted), and size_log may be
> up to 30, so the shift overflows for any table entry >= 4.
>
> Is the signed left shift here reportable under CONFIG_UBSAN_SHIFT, and can
> that be reached by a namespace-local CAP_NET_ADMIN user?
>
> With the usual wrapping behaviour, is the new clamp monotone in the
> attacker-controlled inputs? For example data[slot] = 65535 with
> size_log = 16 wraps to -65536, which clamp_t(int, ...) turns into 1, so
> shaping qdiscs such as TBF, HTB and fq would account one byte for a packet
> the table describes as huge.
>
> Would doing this arithmetic in u64 (or unsigned with saturation) and
> narrowing before the clamp make the new bound actually hold?
>
> >  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;
> >  }
>
> [Severity: High]
> Does this clamp actually bound the DRR/ETS refill loops, or only one of
> their inputs?
>
> drr_dequeue() round-robins the active class list, and each unsuccessful
> visit advances only the head class by one quantum:
>
> net/sched/sch_drr.c:drr_dequeue() {
>         while (1) {
>                 cl = list_first_entry(&q->active, struct drr_class, alist);
>                 skb = cl->qdisc->ops->peek(cl->qdisc);
>                 ...
>                 len = qdisc_pkt_len(skb);
>                 if (len <= cl->deficit) {
>                         ...
>                         return skb;
>                 }
>
>                 WRITE_ONCE(cl->deficit, cl->deficit + READ_ONCE(cl->quantum));
>                 list_move_tail(&cl->alist, &q->active);
>         }
> }
>
> Since every active class advances at the same rate, isn't the iteration
> count before the first packet becomes eligible roughly
> nr_active_classes * (qdisc_pkt_len / quantum) rather than
> qdisc_pkt_len / quantum?
>
> The minimum accepted quantum is 1, since drr_change_class() only rejects
> zero:
>
> net/sched/sch_drr.c:drr_change_class() {
>         quantum = nla_get_u32(tb[TCA_DRR_QUANTUM]);
>         if (quantum == 0) {
>                 NL_SET_ERR_MSG(extack, "Specified DRR quantum cannot be zero");
>                 return -EINVAL;
>         }
> }
>
> and drr_enqueue() re-arms the worst case on every reactivation:
>
> net/sched/sch_drr.c:drr_enqueue() {
>         if (!cl_is_active(cl)) {
>                 list_add_tail(&cl->alist, &q->active);
>                 WRITE_ONCE(cl->deficit, READ_ONCE(cl->quantum));
>         }
> }
>
> So with quantum 1, the new 2^20 cap and roughly 1024 active classes (class
> creation is not bounded, and drr_classify() maps skb->priority to a classid
> so an unprivileged socket can steer packets per class), does a single
> drr_dequeue() still run on the order of 10^9 iterations with the root qdisc
> spinlock held and softirqs disabled?
>
> The same shape appears in ets_qdisc_dequeue(), which is the other consumer
> named in the new comment:
>
> net/sched/sch_ets.c:ets_qdisc_dequeue() {
>                 cl->deficit += READ_ONCE(cl->quantum);
>                 list_move_tail(&cl->alist, &q->active);
> }
>
> with an added per-iteration scan of the q->nstrict strict bands.
>
> Also, is this reachable with no TCA_STAB at all, for instance ~16k classes
> with quantum 1 and ordinary 64 KiB packets? If so, the deficit-loop stall
> does not appear to be gated on size-table amplification in the first place.
>
> And can __qdisc_run() interrupt it?
>
> net/sched/sch_generic.c:__qdisc_run() {
>         while (qdisc_restart(q, &packets, quota)) {
>                 quota -= packets;
>                 if (quota <= 0) {
>                         ...
>                         break;
>                 }
>         }
> }
>
> The quota is only evaluated after qdisc_restart() returns, so it cannot
> preempt the internal refill loop.
>
> Would bounding the loop itself, either with an iteration limit or by
> requiring a quantum that is sane relative to the maximum pkt_len, be
> needed instead of clamping one of its inputs?
> --
> This is an AI-generated review.
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
  2026-08-25  8:14 Jamal Hadi Salim
  2026-08-27 11:01 ` Paolo Abeni
@ 2026-08-27 19:40 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 10+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-08-27 19:40 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, jiri, davem, edumazet, kuba, pabeni, horms, stable, vega,
	victor

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Tue, 25 Aug 2026 04:14:03 -0400 you wrote:
> 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.
> 
> [...]

Here is the summary with links:
  - [net,v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup
    https://git.kernel.org/netdev/net/c/8f735d64382d

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-08-27 19:41 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 14:32 [PATCH net v2] net/sched: bound qdisc_pkt_len to prevent qdisc soft lockup Jamal Hadi Salim
2026-08-19 14:52 ` Eric Dumazet
2026-08-19 14:58   ` Jamal Hadi Salim
2026-08-19 15:15     ` Eric Dumazet
2026-08-19 15:30       ` Jamal Hadi Salim
2026-08-24 18:36         ` Jakub Kicinski
  -- strict thread matches above, loose matches on Subject: below --
2026-08-25  8:14 Jamal Hadi Salim
2026-08-27 11:01 ` Paolo Abeni
2026-08-27 17:48   ` Jamal Hadi Salim
2026-08-27 19:40 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox