Thread (9 messages) 9 messages, 3 authors, 18h ago

Re: [PATCH v2] net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead()

From: Simon Horman <horms@kernel.org>
Date: 2026-09-27 11:29:28
Also in: lkml, stable

On Fri, Sep 25, 2026 at 11:42:45AM +0000, netdev-bot+sashiko@kernel.org wrote:
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider.

Critical: 0 · High: 0 · Medium: 1 · Low: 1

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [Medium] Single `Fixes: a41851bea7bf` tag plus `Cc: stable` for three
  independent defects, none of which was introduced by the tagged commit.
- [Low] The changelog justifies change 3 with "post-enqueue mangling
  (such as BPF packet trimming via bpf_skb_net_shrink())", but that…

--

net/sched: sch_cake: prevent shaper corruption and stall in cake_overhead()

This changes cake_overhead() to bail out to cake_calc_overhead() for
segs <= 1, to skip the multi-segment path when the transport header was
never set, and to treat a negative header offset as a fallback case by
making hdr_len signed. The stated goal is to avoid inflated lengths being
charged to the CAKE shaper.
quoted
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index dc93267029e70..45969c1b95fc9 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1413,21 +1413,28 @@ 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));
 
-	if (segs == 1)
+	if (segs <= 1)
 		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);
+	}
[Severity: Medium]
Should the three changes here carry their own Fixes tags rather than a
single Fixes: a41851bea7bf plus Cc: stable?

None of the three defects look like they were introduced by
a41851bea7bf. git show a41851bea7bf only replaces

	hdr_len = skb_transport_offset(skb);

with the encapsulation if/else, so it neither added the segs handling nor
the missing sentinel check.

The segs early return that changes from segs == 1 to segs <= 1 came in
with c5d34f4583ea88 ("net_sched: cake: use qdisc_pkt_segs()"). Before
that commit cake_overhead() recomputed the count itself for dodgy GSO
skbs:

	segs = DIV_ROUND_UP(skb->len - hdr_len, shinfo->gso_size);

so a cached segs == 0 could not be observed in older trees.

The missing skb_transport_header_was_set() guard, on the other hand,
dates back to a729b7f0bd5bf4, where the same
hdr_len = skb_transport_offset(skb); had the identical ~0U sentinel
behaviour.

With only the a41851bea7bf tag, doesn't the segs <= 1 hunk get applied to
trees that never had the cached count, while the sentinel fix never
reaches the trees between a729b7f0bd5bf4 and a41851bea7bf that are
exposed to the ~66 KB per segment accounting the changelog describes?
The above analysis does seem to be correct.
Would splitting this into one logical fix per patch, each with its own
Fixes tag, make the stable ranges match the defects?
And, yes, I think one fix per patch is the right approach.
quoted
+
+	if (unlikely(hdr_len < 0))
+		return cake_calc_overhead(q, len, off);
 
 	/* + transport layer */
 	if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 |
[Severity: Low]
The changelog justifies this guard with:

    If post-enqueue mangling (such as BPF packet trimming via
    bpf_skb_net_shrink()) produces a negative offset, hdr_len wraps to
    near UINT_MAX

Is that ordering possible? __dev_queue_xmit() calls

	reason = qdisc_pkt_len_segs_init(skb);

near its top, and only afterwards runs sch_handle_egress() and
__dev_xmit_skb() -> cake_enqueue() -> cake_overhead(), which is the only
caller. So cake_overhead() runs during enqueue, not after it.

The two windows that do exist look like:

  - between qdisc_pkt_len_segs_init() and enqueue, i.e. tc/BPF egress in
    sch_handle_egress()
  - inside cake_enqueue() itself, since cake_classify() -> tcf_classify()
    (act_bpf, act_pedit, act_mpls, act_vlan) runs before
    get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb)

The underlying mechanism still holds: bpf_skb_adjust_room() with
BPF_ADJ_ROOM_MAC ends up in bpf_skb_net_hdr_pop(), which adjusts only
mac_header/network_header and leaves transport_header stale, so
skb_transport_offset() becomes old_offset - len_diff and can go negative.

Could the changelog name that ordering and a concrete path instead of
"post-enqueue mangling", given the patch is tagged Cc: stable?
FWIIW, I think this is less of a concern.

-- 
pw-bot: changes-requested
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help