* [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets
@ 2026-10-22 15:54 Eric Dumazet
2026-10-22 16:03 ` Michael S. Tsirkin
2026-10-26 23:30 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-22 15:54 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Willem de Bruijn, Simon Horman, netdev, Eric Dumazet, shaos,
Jamal Hadi Salim, Michael S. Tsirkin
An SKB_GSO_DODGY packet can carry untrusted gso_size and headers.
For instance, gso_size=1 and hdr_len=65532 for a 524275-byte frame
makes qdisc_pkt_len_segs_init() compute an accounted length of
about 4,294,574,091 bytes (after gso_segs silently wrapped in a u16).
With DRR quantum 256, the dequeue path then needs ~16.8 million
deficit refills before it can dequeue the skb, keeping the CPU in
the qdisc path long enough to trigger the soft-lockup watchdog.
Commit 8f735d64382d ("net/sched: bound qdisc_pkt_len to prevent
qdisc soft lockup") added QDISC_PKT_LEN_MAX to bound size table
amplification. Use the same bound for DODGY packets:
- Make gso_segs a u32 and drop packets whose segment count does
not fit GSO_MAX_SEGS, instead of silently truncating it.
- Drop packets whose estimated qdisc_pkt_len() would exceed
QDISC_PKT_LEN_MAX.
Fixes: 15e5a0307164 ("net_sched: better precise estimation on packet length for untrusted packets")
Reported-by: shaos <shawnq1@163.com>
Closes: https://lore.kernel.org/netdev/20261022144755.65025-1-shawnq1@163.com/
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: "Michael S. Tsirkin" <mst@redhat.com>
---
net/core/dev.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index e76762e29360ea87ade0846e4a4f9016dca587d6..573bcc9d67b1b1e21e844f054f0b51a172713a90 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4175,7 +4175,7 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
unsigned int hdr_len, tlen;
- u16 gso_segs;
+ u32 gso_segs;
qdisc_skb_cb(skb)->pkt_len = skb->len;
if (!shinfo->gso_size) {
@@ -4226,6 +4226,15 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
if (payload <= 0)
return SKB_DROP_REASON_SKB_BAD_GSO;
gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size);
+ /* shinfo->gso_segs is a 16bit field.
+ * Also make sure untrusted gso_size/hdr_len can not inflate
+ * qdisc_pkt_len() beyond QDISC_PKT_LEN_MAX, or deficit
+ * schedulers could spin in their refill loops.
+ */
+ if (unlikely(gso_segs > GSO_MAX_SEGS ||
+ skb->len + (u64)(gso_segs - 1) * hdr_len >
+ QDISC_PKT_LEN_MAX))
+ return SKB_DROP_REASON_SKB_BAD_GSO;
shinfo->gso_segs = gso_segs;
qdisc_skb_cb(skb)->pkt_segs = gso_segs;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets
2026-10-22 15:54 [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets Eric Dumazet
@ 2026-10-22 16:03 ` Michael S. Tsirkin
2026-10-23 1:29 ` Willem de Bruijn
2026-10-26 23:30 ` patchwork-bot+netdevbpf
1 sibling, 1 reply; 4+ messages in thread
From: Michael S. Tsirkin @ 2026-10-22 16:03 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Willem de Bruijn,
Simon Horman, netdev, shaos, Jamal Hadi Salim
On Thu, Oct 22, 2026 at 05:54:36PM +0200, Eric Dumazet wrote:
> An SKB_GSO_DODGY packet can carry untrusted gso_size and headers.
>
> For instance, gso_size=1 and hdr_len=65532 for a 524275-byte frame
> makes qdisc_pkt_len_segs_init() compute an accounted length of
> about 4,294,574,091 bytes (after gso_segs silently wrapped in a u16).
>
> With DRR quantum 256, the dequeue path then needs ~16.8 million
> deficit refills before it can dequeue the skb, keeping the CPU in
> the qdisc path long enough to trigger the soft-lockup watchdog.
>
> Commit 8f735d64382d ("net/sched: bound qdisc_pkt_len to prevent
> qdisc soft lockup") added QDISC_PKT_LEN_MAX to bound size table
> amplification. Use the same bound for DODGY packets:
>
> - Make gso_segs a u32 and drop packets whose segment count does
> not fit GSO_MAX_SEGS, instead of silently truncating it.
>
> - Drop packets whose estimated qdisc_pkt_len() would exceed
> QDISC_PKT_LEN_MAX.
>
> Fixes: 15e5a0307164 ("net_sched: better precise estimation on packet length for untrusted packets")
> Reported-by: shaos <shawnq1@163.com>
> Closes: https://lore.kernel.org/netdev/20261022144755.65025-1-shawnq1@163.com/
> Signed-off-by: Eric Dumazet <edumazet@kernel.org>
> Cc: Jamal Hadi Salim <jhs@mojatatu.com>
> Cc: "Michael S. Tsirkin" <mst@redhat.com>
I'm all for stack being more resilient.
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> ---
> net/core/dev.c | 11 ++++++++++-
> 1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index e76762e29360ea87ade0846e4a4f9016dca587d6..573bcc9d67b1b1e21e844f054f0b51a172713a90 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -4175,7 +4175,7 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
> {
> struct skb_shared_info *shinfo = skb_shinfo(skb);
> unsigned int hdr_len, tlen;
> - u16 gso_segs;
> + u32 gso_segs;
>
> qdisc_skb_cb(skb)->pkt_len = skb->len;
> if (!shinfo->gso_size) {
> @@ -4226,6 +4226,15 @@ static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
> if (payload <= 0)
> return SKB_DROP_REASON_SKB_BAD_GSO;
> gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size);
> + /* shinfo->gso_segs is a 16bit field.
> + * Also make sure untrusted gso_size/hdr_len can not inflate
> + * qdisc_pkt_len() beyond QDISC_PKT_LEN_MAX, or deficit
> + * schedulers could spin in their refill loops.
> + */
> + if (unlikely(gso_segs > GSO_MAX_SEGS ||
> + skb->len + (u64)(gso_segs - 1) * hdr_len >
> + QDISC_PKT_LEN_MAX))
> + return SKB_DROP_REASON_SKB_BAD_GSO;
> shinfo->gso_segs = gso_segs;
> qdisc_skb_cb(skb)->pkt_segs = gso_segs;
> }
> --
> 2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets
2026-10-22 16:03 ` Michael S. Tsirkin
@ 2026-10-23 1:29 ` Willem de Bruijn
0 siblings, 0 replies; 4+ messages in thread
From: Willem de Bruijn @ 2026-10-23 1:29 UTC (permalink / raw)
To: Michael S. Tsirkin, Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Willem de Bruijn,
Simon Horman, netdev, shaos, Jamal Hadi Salim
Michael S. Tsirkin wrote:
> On Thu, Oct 22, 2026 at 05:54:36PM +0200, Eric Dumazet wrote:
> > An SKB_GSO_DODGY packet can carry untrusted gso_size and headers.
> >
> > For instance, gso_size=1 and hdr_len=65532 for a 524275-byte frame
> > makes qdisc_pkt_len_segs_init() compute an accounted length of
> > about 4,294,574,091 bytes (after gso_segs silently wrapped in a u16).
> >
> > With DRR quantum 256, the dequeue path then needs ~16.8 million
> > deficit refills before it can dequeue the skb, keeping the CPU in
> > the qdisc path long enough to trigger the soft-lockup watchdog.
> >
> > Commit 8f735d64382d ("net/sched: bound qdisc_pkt_len to prevent
> > qdisc soft lockup") added QDISC_PKT_LEN_MAX to bound size table
> > amplification. Use the same bound for DODGY packets:
> >
> > - Make gso_segs a u32 and drop packets whose segment count does
> > not fit GSO_MAX_SEGS, instead of silently truncating it.
> >
> > - Drop packets whose estimated qdisc_pkt_len() would exceed
> > QDISC_PKT_LEN_MAX.
> >
> > Fixes: 15e5a0307164 ("net_sched: better precise estimation on packet length for untrusted packets")
> > Reported-by: shaos <shawnq1@163.com>
> > Closes: https://lore.kernel.org/netdev/20261022144755.65025-1-shawnq1@163.com/
> > Signed-off-by: Eric Dumazet <edumazet@kernel.org>
> > Cc: Jamal Hadi Salim <jhs@mojatatu.com>
> > Cc: "Michael S. Tsirkin" <mst@redhat.com>
>
> I'm all for stack being more resilient.
>
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets
2026-10-22 15:54 [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets Eric Dumazet
2026-10-22 16:03 ` Michael S. Tsirkin
@ 2026-10-26 23:30 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 4+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-10-26 23:30 UTC (permalink / raw)
To: Eric Dumazet
Cc: davem, kuba, pabeni, willemb, horms, netdev, shawnq1, jhs, mst
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Thu, 22 Oct 2026 17:54:36 +0200 you wrote:
> An SKB_GSO_DODGY packet can carry untrusted gso_size and headers.
>
> For instance, gso_size=1 and hdr_len=65532 for a 524275-byte frame
> makes qdisc_pkt_len_segs_init() compute an accounted length of
> about 4,294,574,091 bytes (after gso_segs silently wrapped in a u16).
>
> With DRR quantum 256, the dequeue path then needs ~16.8 million
> deficit refills before it can dequeue the skb, keeping the CPU in
> the qdisc path long enough to trigger the soft-lockup watchdog.
>
> [...]
Here is the summary with links:
- [net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets
https://git.kernel.org/netdev/net/c/c6648c9f3b51
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] 4+ messages in thread
end of thread, other threads:[~2026-10-26 23:30 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-22 15:54 [PATCH net] net: harden qdisc_pkt_len_segs_init() against malicious GSO packets Eric Dumazet
2026-10-22 16:03 ` Michael S. Tsirkin
2026-10-23 1:29 ` Willem de Bruijn
2026-10-26 23:30 ` 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