mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v3 0/2] net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead()
@ 2026-09-27 13:10 Yuchao Zhang
  2026-09-27 13:10 ` [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 " Yuchao Zhang
  2026-09-27 13:10 ` [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset " Yuchao Zhang
  0 siblings, 2 replies; 5+ messages in thread
From: Yuchao Zhang @ 2026-09-27 13:10 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, cake, netdev,
	linux-kernel, stable, Yuchao Zhang

This series addresses two issues in cake_overhead() that can lead to
corrupted rate shaper accounting or long dequeue stalls:

Patch 1 fixes an integer underflow when segs == 0. When an skb with
segs == 0 (such as dodgy GSO packets where gso_segs is not recomputed)
reaches cake_overhead(), (segs - 1) wraps around to UINT32_MAX,
multiplying per-segment overhead by ~4.29 billion and returning a length
close to 4.29 GB. cake_advance_shaper() then charges that length to the
shaper, stalling the dequeue queue for tens of seconds at 1 Gbit/s, and
for minutes to hours at lower rates. This was introduced by commit
c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()").

Patch 2 validates the transport header offset computed in cake_overhead().
When the transport header was never set, skb_transport_offset() returns
the ~0U sentinel (~65535), which inflates shaper accounting by ~66 KB
per segment. Furthermore, if preceding egress BPF filters (e.g.
sch_handle_egress()) or cake classifier actions (e.g. act_bpf trimming
headers via bpf_skb_adjust_room(BPF_ADJ_ROOM_MAC)) leave the transport
header stale (bpf_skb_net_hdr_pop() only re-syncs it when it aliased
network_header), skb_transport_offset() can become negative. Because
hdr_len was declared as unsigned int, a negative offset wraps to near
UINT_MAX. Patch 2 checks !skb_transport_header_was_set() and ensures
hdr_len >= 0. Both hunks date back to commit a729b7f0bd5b ("sch_cake:
Add overhead compensation support to the rate shaper"), so they share a
single Fixes: tag and stable range.

Changes in v3:
  - Split the v2 patch into a 2-patch series per Simon Horman and Sashiko
    AI review so each logical fix carries its own accurate Fixes: tag and
    matches proper stable tree backport ranges:
    - Patch 1 Fixes: c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()")
    - Patch 2 Fixes: a729b7f0bd5b ("sch_cake: Add overhead
      compensation support to the rate shaper")
  - Clarify the timing and code paths where header mangling can occur
    (sch_handle_egress() and cake_classify() before cake_overhead()) rather
    than inaccurate "post-enqueue mangling" wording.
  - Link to v2: https://lore.kernel.org/netdev/20260922084124.36858-1-ndaugoing@gmail.com/

Changes in v2:
  - Accurately describe the impact as shaper accounting corruption / stall
    rather than OOB read past the allocation.
  - Fix integer underflow when segs == 0 by checking segs <= 1.
  - Import companion check !skb_transport_header_was_set(skb) from
    qdisc_pkt_len_segs_init() to prevent unset transport header sentinel
    (~0U) from inflating packet length to ~66 KB.
  - Link to v1: https://lore.kernel.org/netdev/20260917122153.62722-1-ndaugoing@gmail.com/

Yuchao Zhang (2):
  net/sched: sch_cake: fix shaper stall on segs == 0 in cake_overhead()
  net/sched: sch_cake: validate transport header offset in
    cake_overhead()

 net/sched/sch_cake.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

-- 
2.53.0


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

* [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 in cake_overhead()
  2026-09-27 13:10 [PATCH net v3 0/2] net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead() Yuchao Zhang
@ 2026-09-27 13:10 ` Yuchao Zhang
  2026-09-28 12:30   ` Toke Høiland-Jørgensen
  2026-09-27 13:10 ` [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset " Yuchao Zhang
  1 sibling, 1 reply; 5+ messages in thread
From: Yuchao Zhang @ 2026-09-27 13:10 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, cake, netdev,
	linux-kernel, stable, Yuchao Zhang

In cake_overhead(), packets with a single segment bypass multi-segment
overhead calculations:

	if (segs == 1)
		return cake_calc_overhead(q, len, off);

Commit c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()") switched
cake to retrieve the cached segmentation count via qdisc_pkt_segs(skb)
instead of calculating it locally for dodgy GSO packets. If an skb with
segs == 0 reaches cake_overhead(), it skips the segs == 1 early return
and enters the multi-segment arithmetic:

	len = shinfo->gso_size + hdr_len;
	last_len = skb->len - shinfo->gso_size * (segs - 1);

	return (cake_calc_overhead(q, len, off) * (segs - 1) +
		cake_calc_overhead(q, last_len, off));

Because segs is an unsigned 16-bit integer, (segs - 1) underflows to
65535 (and is promoted to 4294967295 in the 32-bit unsigned arithmetic
above). This multiplies the per-segment overhead by UINT32_MAX, so
cake_overhead() returns a length close to 4.29 GB. cake_advance_shaper()
then charges that length to the shaper, stalling the CAKE dequeue path
for tens of seconds at 1 Gbit/s, and for minutes to hours at lower rates.

Fix this by returning early with cake_calc_overhead(q, len, off) whenever
segs <= 1.

Fixes: c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()")
Cc: stable@vger.kernel.org
Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com>
---
v3:
 - Split from v2 into a standalone patch with its own Fixes: tag
   (c5d34f4583ea) per Simon Horman and Sashiko review.
 - Link to v2: https://lore.kernel.org/netdev/20260922084124.36858-1-ndaugoing@gmail.com/
 - Link to v1: https://lore.kernel.org/netdev/20260917122153.62722-1-ndaugoing@gmail.com/

 net/sched/sch_cake.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index dc93267029e7..b0d604a7052a 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1420,7 +1420,7 @@ static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
 
 	WRITE_ONCE(q->avg_netoff, cake_ewma(q->avg_netoff, off << 16, 8));
 
-	if (segs == 1)
+	if (segs <= 1)
 		return cake_calc_overhead(q, len, off);
 
 	/* borrowed from qdisc_pkt_len_segs_init() */
-- 
2.53.0


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

* [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset in cake_overhead()
  2026-09-27 13:10 [PATCH net v3 0/2] net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead() Yuchao Zhang
  2026-09-27 13:10 ` [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 " Yuchao Zhang
@ 2026-09-27 13:10 ` Yuchao Zhang
  2026-09-28 12:32   ` Toke Høiland-Jørgensen
  1 sibling, 1 reply; 5+ messages in thread
From: Yuchao Zhang @ 2026-09-27 13:10 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, cake, netdev,
	linux-kernel, stable, Yuchao Zhang

In cake_overhead(), the header length up to the transport layer is
computed using logic borrowed from qdisc_pkt_len_segs_init():

	/* borrowed from qdisc_pkt_len_segs_init() */
	if (!skb->encapsulation)
		hdr_len = skb_transport_offset(skb);
	else
		hdr_len = skb_inner_transport_offset(skb);

However, cake_overhead() does not validate the computed offset:

1. When the transport header was never set, skb->transport_header holds
   the sentinel value ~0U. skb_transport_offset() returns ~65535.
   skb_header_pointer() subsequently fails, leaving hdr_len as ~65535,
   charging ~66 KB per segment to the shaper.
   Mirror qdisc_pkt_len_segs_init() by returning cake_calc_overhead()
   when unlikely(!skb_transport_header_was_set(skb)).

2. While qdisc_pkt_len_segs_init() runs at the start of __dev_queue_xmit(),
   packet headers may be adjusted before cake_overhead() is reached:
   - in sch_handle_egress() via tc/BPF egress filters;
   - inside cake_enqueue() via cake_classify() -> tcf_classify() (e.g.
     act_bpf, act_pedit, act_mpls, act_vlan).
   For example, bpf_skb_adjust_room(..., BPF_ADJ_ROOM_MAC) invokes
   bpf_skb_net_hdr_pop(), which pulls skb->data forward and re-syncs
   transport_header only when it aliased network_header, i.e. when no
   transport header had been parsed. If a transport header had been
   parsed, its offset is left where it was while skb->data moves forward,
   so skb_transport_offset() becomes old_offset - len and can turn
   negative. Since hdr_len was declared as unsigned int, a negative offset
   wraps around to near UINT_MAX, corrupting header length accounting.

Declare hdr_len as int and fall back to cake_calc_overhead(q, len, off) if
unlikely(hdr_len < 0).

Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper")
Cc: stable@vger.kernel.org
Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com>
---
v3:
 - Split from v2 into a standalone patch with its own Fixes: tag
   (a729b7f0bd5b) per Simon Horman and Sashiko review.
 - Clarify header mangling ordering (sch_handle_egress() and cake_classify()
   before cake_overhead()) rather than inaccurate "post-enqueue mangling"
   wording per Sashiko review.
 - Link to v2: https://lore.kernel.org/netdev/20260922084124.36858-1-ndaugoing@gmail.com/
 - Link to v1: https://lore.kernel.org/netdev/20260917122153.62722-1-ndaugoing@gmail.com/

 net/sched/sch_cake.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index b0d604a7052a..45969c1b95fc 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1413,10 +1413,11 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off)
 static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
 {
 	const struct skb_shared_info *shinfo = skb_shinfo(skb);
-	unsigned int hdr_len, last_len = 0;
+	unsigned int last_len = 0;
 	u32 off = skb_network_offset(skb);
 	u16 segs = qdisc_pkt_segs(skb);
 	u32 len = qdisc_pkt_len(skb);
+	int hdr_len;
 
 	WRITE_ONCE(q->avg_netoff, cake_ewma(q->avg_netoff, off << 16, 8));
 
@@ -1424,10 +1425,16 @@ static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
 		return cake_calc_overhead(q, len, off);
 
 	/* borrowed from qdisc_pkt_len_segs_init() */
-	if (!skb->encapsulation)
+	if (!skb->encapsulation) {
+		if (unlikely(!skb_transport_header_was_set(skb)))
+			return cake_calc_overhead(q, len, off);
 		hdr_len = skb_transport_offset(skb);
-	else
+	} else {
 		hdr_len = skb_inner_transport_offset(skb);
+	}
+
+	if (unlikely(hdr_len < 0))
+		return cake_calc_overhead(q, len, off);
 
 	/* + transport layer */
 	if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 |
-- 
2.53.0


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

* Re: [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 in cake_overhead()
  2026-09-27 13:10 ` [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 " Yuchao Zhang
@ 2026-09-28 12:30   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 5+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-09-28 12:30 UTC (permalink / raw)
  To: Yuchao Zhang, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, cake, netdev,
	linux-kernel, stable, Yuchao Zhang

Yuchao Zhang <ndaugoing@gmail.com> writes:

> In cake_overhead(), packets with a single segment bypass multi-segment
> overhead calculations:
>
> 	if (segs == 1)
> 		return cake_calc_overhead(q, len, off);
>
> Commit c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()") switched
> cake to retrieve the cached segmentation count via qdisc_pkt_segs(skb)
> instead of calculating it locally for dodgy GSO packets. If an skb with
> segs == 0 reaches cake_overhead(), it skips the segs == 1 early return
> and enters the multi-segment arithmetic:
>
> 	len = shinfo->gso_size + hdr_len;
> 	last_len = skb->len - shinfo->gso_size * (segs - 1);
>
> 	return (cake_calc_overhead(q, len, off) * (segs - 1) +
> 		cake_calc_overhead(q, last_len, off));
>
> Because segs is an unsigned 16-bit integer, (segs - 1) underflows to
> 65535 (and is promoted to 4294967295 in the 32-bit unsigned arithmetic
> above). This multiplies the per-segment overhead by UINT32_MAX, so
> cake_overhead() returns a length close to 4.29 GB. cake_advance_shaper()
> then charges that length to the shaper, stalling the CAKE dequeue path
> for tens of seconds at 1 Gbit/s, and for minutes to hours at lower rates.
>
> Fix this by returning early with cake_calc_overhead(q, len, off) whenever
> segs <= 1.
>
> Fixes: c5d34f4583ea ("net_sched: cake: use qdisc_pkt_segs()")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com>

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

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

* Re: [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset in cake_overhead()
  2026-09-27 13:10 ` [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset " Yuchao Zhang
@ 2026-09-28 12:32   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 5+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-09-28 12:32 UTC (permalink / raw)
  To: Yuchao Zhang, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, cake, netdev,
	linux-kernel, stable, Yuchao Zhang

Yuchao Zhang <ndaugoing@gmail.com> writes:

> In cake_overhead(), the header length up to the transport layer is
> computed using logic borrowed from qdisc_pkt_len_segs_init():
>
> 	/* borrowed from qdisc_pkt_len_segs_init() */
> 	if (!skb->encapsulation)
> 		hdr_len = skb_transport_offset(skb);
> 	else
> 		hdr_len = skb_inner_transport_offset(skb);
>
> However, cake_overhead() does not validate the computed offset:
>
> 1. When the transport header was never set, skb->transport_header holds
>    the sentinel value ~0U. skb_transport_offset() returns ~65535.
>    skb_header_pointer() subsequently fails, leaving hdr_len as ~65535,
>    charging ~66 KB per segment to the shaper.
>    Mirror qdisc_pkt_len_segs_init() by returning cake_calc_overhead()
>    when unlikely(!skb_transport_header_was_set(skb)).
>
> 2. While qdisc_pkt_len_segs_init() runs at the start of __dev_queue_xmit(),
>    packet headers may be adjusted before cake_overhead() is reached:
>    - in sch_handle_egress() via tc/BPF egress filters;
>    - inside cake_enqueue() via cake_classify() -> tcf_classify() (e.g.
>      act_bpf, act_pedit, act_mpls, act_vlan).
>    For example, bpf_skb_adjust_room(..., BPF_ADJ_ROOM_MAC) invokes
>    bpf_skb_net_hdr_pop(), which pulls skb->data forward and re-syncs
>    transport_header only when it aliased network_header, i.e. when no
>    transport header had been parsed. If a transport header had been
>    parsed, its offset is left where it was while skb->data moves forward,
>    so skb_transport_offset() becomes old_offset - len and can turn
>    negative. Since hdr_len was declared as unsigned int, a negative offset
>    wraps around to near UINT_MAX, corrupting header length accounting.
>
> Declare hdr_len as int and fall back to cake_calc_overhead(q, len, off) if
> unlikely(hdr_len < 0).
>
> Fixes: a729b7f0bd5b ("sch_cake: Add overhead compensation support to the rate shaper")
> Cc: stable@vger.kernel.org
> Signed-off-by: Yuchao Zhang <ndaugoing@gmail.com>
> ---
> v3:
>  - Split from v2 into a standalone patch with its own Fixes: tag
>    (a729b7f0bd5b) per Simon Horman and Sashiko review.
>  - Clarify header mangling ordering (sch_handle_egress() and cake_classify()
>    before cake_overhead()) rather than inaccurate "post-enqueue mangling"
>    wording per Sashiko review.
>  - Link to v2: https://lore.kernel.org/netdev/20260922084124.36858-1-ndaugoing@gmail.com/
>  - Link to v1: https://lore.kernel.org/netdev/20260917122153.62722-1-ndaugoing@gmail.com/
>
>  net/sched/sch_cake.c | 13 ++++++++++---
>  1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
> index b0d604a7052a..45969c1b95fc 100644
> --- a/net/sched/sch_cake.c
> +++ b/net/sched/sch_cake.c
> @@ -1413,10 +1413,11 @@ static u32 cake_calc_overhead(struct cake_sched_data *qd, u32 len, u32 off)
>  static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
>  {
>  	const struct skb_shared_info *shinfo = skb_shinfo(skb);
> -	unsigned int hdr_len, last_len = 0;
> +	unsigned int last_len = 0;
>  	u32 off = skb_network_offset(skb);
>  	u16 segs = qdisc_pkt_segs(skb);
>  	u32 len = qdisc_pkt_len(skb);
> +	int hdr_len;
>  
>  	WRITE_ONCE(q->avg_netoff, cake_ewma(q->avg_netoff, off << 16, 8));
>  
> @@ -1424,10 +1425,16 @@ static u32 cake_overhead(struct cake_sched_data *q, const struct sk_buff *skb)
>  		return cake_calc_overhead(q, len, off);
>  
>  	/* borrowed from qdisc_pkt_len_segs_init() */
> -	if (!skb->encapsulation)
> +	if (!skb->encapsulation) {
> +		if (unlikely(!skb_transport_header_was_set(skb)))
> +			return cake_calc_overhead(q, len, off);
>  		hdr_len = skb_transport_offset(skb);
> -	else
> +	} else {
>  		hdr_len = skb_inner_transport_offset(skb);
> +	}
> +
> +	if (unlikely(hdr_len < 0))
> +		return cake_calc_overhead(q, len, off);

We now have three identical calls to cake_calc_overhead() in the same
function; let's put these into an 'err' label at the end of the
function, and turn the early returns into 'goto err' statements.

-Toke

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

end of thread, other threads:[~2026-09-28 13:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-27 13:10 [PATCH net v3 0/2] net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead() Yuchao Zhang
2026-09-27 13:10 ` [PATCH net v3 1/2] net/sched: sch_cake: fix shaper stall on segs == 0 " Yuchao Zhang
2026-09-28 12:30   ` Toke Høiland-Jørgensen
2026-09-27 13:10 ` [PATCH net v3 2/2] net/sched: sch_cake: validate transport header offset " Yuchao Zhang
2026-09-28 12:32   ` Toke Høiland-Jørgensen

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

all inboxes | Powered by JetHome®