* [Cake] [PATCH net v4] net/sched: sch_cake: Fix incorrect qlen reduction in cake_drop
@ 2025-11-21 21:20 Xiang Mei
2025-11-21 21:26 ` [Cake] " Xiang Mei
0 siblings, 1 reply; 2+ messages in thread
From: Xiang Mei @ 2025-11-21 21:20 UTC (permalink / raw)
To: security; +Cc: netdev, toke, xiyou.wangcong, cake, bestswngs, Xiang Mei
In cake_drop(), qdisc_tree_reduce_backlog() is used to update the qlen
and backlog of the qdisc hierarchy. Its caller, cake_enqueue(), assumes
that the parent qdisc will enqueue the current packet. However, this
assumption breaks when cake_enqueue() returns NET_XMIT_CN: the parent
qdisc stops enqueuing current packet, leaving the tree qlen/backlog
accounting inconsistent. This mismatch can lead to a NULL dereference
(e.g., when the parent Qdisc is qfq_qdisc).
This patch computes the qlen/backlog delta in a more robust way by
observing the difference before and after the series of cake_drop()
calls, and then compensates the qdisc tree accounting if cake_enqueue()
returns NET_XMIT_CN.
To ensure correct compensation when ACK thinning is enabled, a new
variable is introduced to keep qlen unchanged.
Fixes: 15de71d06a40 ("net/sched: Make cake_enqueue return NET_XMIT_CN when past buffer_limit")
Signed-off-by: Xiang Mei <xmei5@asu.edu>
---
v2: add missing cc
v3: move qdisc_tree_reduce_backlog out of cake_drop
v4: remove redundant variable and handle ack branch correctly
---
net/sched/sch_cake.c | 52 +++++++++++++++++++++++++-------------------
1 file changed, 30 insertions(+), 22 deletions(-)
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 32bacfc314c2..cf4d6454ca9c 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -1597,7 +1597,6 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)
qdisc_drop_reason(skb, sch, to_free, SKB_DROP_REASON_QDISC_OVERLIMIT);
sch->q.qlen--;
- qdisc_tree_reduce_backlog(sch, 1, len);
cake_heapify(q, 0);
@@ -1750,7 +1749,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
ktime_t now = ktime_get();
struct cake_tin_data *b;
struct cake_flow *flow;
- u32 idx, tin;
+ u32 idx, tin, prev_qlen, prev_backlog, drop_id;
+ bool same_flow = false;
/* choose flow to insert into */
idx = cake_classify(sch, &b, skb, q->flow_mode, &ret);
@@ -1823,6 +1823,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
consume_skb(skb);
} else {
/* not splitting */
+ int ack_pkt_len = 0;
+
cobalt_set_enqueue_time(skb, now);
get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb);
flow_queue_add(flow, skb);
@@ -1834,7 +1836,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
b->ack_drops++;
sch->qstats.drops++;
b->bytes += qdisc_pkt_len(ack);
- len -= qdisc_pkt_len(ack);
+ ack_pkt_len = qdisc_pkt_len(ack);
q->buffer_used += skb->truesize - ack->truesize;
if (q->rate_flags & CAKE_FLAG_INGRESS)
cake_advance_shaper(q, b, ack, now, true);
@@ -1848,11 +1850,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
/* stats */
b->packets++;
- b->bytes += len;
- b->backlogs[idx] += len;
- b->tin_backlog += len;
- sch->qstats.backlog += len;
- q->avg_window_bytes += len;
+ b->bytes += len - ack_pkt_len;
+ b->backlogs[idx] += len - ack_pkt_len;
+ b->tin_backlog += len - ack_pkt_len;
+ sch->qstats.backlog += len - ack_pkt_len;
+ q->avg_window_bytes += len - ack_pkt_len;
}
if (q->overflow_timeout)
@@ -1927,24 +1929,30 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
if (q->buffer_used > q->buffer_max_used)
q->buffer_max_used = q->buffer_used;
- if (q->buffer_used > q->buffer_limit) {
- bool same_flow = false;
- u32 dropped = 0;
- u32 drop_id;
+ if (q->buffer_used <= q->buffer_limit)
+ return NET_XMIT_SUCCESS;
- while (q->buffer_used > q->buffer_limit) {
- dropped++;
- drop_id = cake_drop(sch, to_free);
+ prev_qlen = sch->q.qlen;
+ prev_backlog = sch->qstats.backlog;
- if ((drop_id >> 16) == tin &&
- (drop_id & 0xFFFF) == idx)
- same_flow = true;
- }
- b->drop_overlimit += dropped;
+ while (q->buffer_used > q->buffer_limit) {
+ drop_id = cake_drop(sch, to_free);
+ if ((drop_id >> 16) == tin &&
+ (drop_id & 0xFFFF) == idx)
+ same_flow = true;
+ }
+
+ /* Compute the droppped qlen and pkt length */
+ prev_qlen -= sch->q.qlen;
+ prev_backlog -= sch->qstats.backlog;
+ b->drop_overlimit += prev_backlog;
- if (same_flow)
- return NET_XMIT_CN;
+ if (same_flow) {
+ qdisc_tree_reduce_backlog(sch, prev_qlen - 1,
+ prev_backlog - len);
+ return NET_XMIT_CN;
}
+ qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog);
return NET_XMIT_SUCCESS;
}
--
2.43.0
^ permalink raw reply [flat|nested] 2+ messages in thread
* [Cake] Re: [PATCH net v4] net/sched: sch_cake: Fix incorrect qlen reduction in cake_drop
2025-11-21 21:20 [Cake] [PATCH net v4] net/sched: sch_cake: Fix incorrect qlen reduction in cake_drop Xiang Mei
@ 2025-11-21 21:26 ` Xiang Mei
0 siblings, 0 replies; 2+ messages in thread
From: Xiang Mei @ 2025-11-21 21:26 UTC (permalink / raw)
To: security; +Cc: netdev, toke, xiyou.wangcong, cake, bestswngs
On Fri, Nov 21, 2025 at 03:19:54PM -0700, Xiang Mei wrote:
> In cake_drop(), qdisc_tree_reduce_backlog() is used to update the qlen
> and backlog of the qdisc hierarchy. Its caller, cake_enqueue(), assumes
> that the parent qdisc will enqueue the current packet. However, this
> assumption breaks when cake_enqueue() returns NET_XMIT_CN: the parent
> qdisc stops enqueuing current packet, leaving the tree qlen/backlog
> accounting inconsistent. This mismatch can lead to a NULL dereference
> (e.g., when the parent Qdisc is qfq_qdisc).
>
> This patch computes the qlen/backlog delta in a more robust way by
> observing the difference before and after the series of cake_drop()
> calls, and then compensates the qdisc tree accounting if cake_enqueue()
> returns NET_XMIT_CN.
>
> To ensure correct compensation when ACK thinning is enabled, a new
> variable is introduced to keep qlen unchanged.
>
> Fixes: 15de71d06a40 ("net/sched: Make cake_enqueue return NET_XMIT_CN when past buffer_limit")
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
> ---
> v2: add missing cc
> v3: move qdisc_tree_reduce_backlog out of cake_drop
> v4: remove redundant variable and handle ack branch correctly
> ---
> net/sched/sch_cake.c | 52 +++++++++++++++++++++++++-------------------
> 1 file changed, 30 insertions(+), 22 deletions(-)
>
> diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
> index 32bacfc314c2..cf4d6454ca9c 100644
> --- a/net/sched/sch_cake.c
> +++ b/net/sched/sch_cake.c
> @@ -1597,7 +1597,6 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free)
>
> qdisc_drop_reason(skb, sch, to_free, SKB_DROP_REASON_QDISC_OVERLIMIT);
> sch->q.qlen--;
> - qdisc_tree_reduce_backlog(sch, 1, len);
>
> cake_heapify(q, 0);
>
> @@ -1750,7 +1749,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> ktime_t now = ktime_get();
> struct cake_tin_data *b;
> struct cake_flow *flow;
> - u32 idx, tin;
> + u32 idx, tin, prev_qlen, prev_backlog, drop_id;
> + bool same_flow = false;
>
> /* choose flow to insert into */
> idx = cake_classify(sch, &b, skb, q->flow_mode, &ret);
> @@ -1823,6 +1823,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> consume_skb(skb);
> } else {
> /* not splitting */
> + int ack_pkt_len = 0;
> +
> cobalt_set_enqueue_time(skb, now);
> get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb);
> flow_queue_add(flow, skb);
> @@ -1834,7 +1836,7 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> b->ack_drops++;
> sch->qstats.drops++;
> b->bytes += qdisc_pkt_len(ack);
> - len -= qdisc_pkt_len(ack);
> + ack_pkt_len = qdisc_pkt_len(ack);
> q->buffer_used += skb->truesize - ack->truesize;
> if (q->rate_flags & CAKE_FLAG_INGRESS)
> cake_advance_shaper(q, b, ack, now, true);
> @@ -1848,11 +1850,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
>
> /* stats */
> b->packets++;
> - b->bytes += len;
> - b->backlogs[idx] += len;
> - b->tin_backlog += len;
> - sch->qstats.backlog += len;
> - q->avg_window_bytes += len;
> + b->bytes += len - ack_pkt_len;
> + b->backlogs[idx] += len - ack_pkt_len;
> + b->tin_backlog += len - ack_pkt_len;
> + sch->qstats.backlog += len - ack_pkt_len;
> + q->avg_window_bytes += len - ack_pkt_len;
> }
>
> if (q->overflow_timeout)
> @@ -1927,24 +1929,30 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> if (q->buffer_used > q->buffer_max_used)
> q->buffer_max_used = q->buffer_used;
>
> - if (q->buffer_used > q->buffer_limit) {
> - bool same_flow = false;
> - u32 dropped = 0;
> - u32 drop_id;
> + if (q->buffer_used <= q->buffer_limit)
> + return NET_XMIT_SUCCESS;
>
> - while (q->buffer_used > q->buffer_limit) {
> - dropped++;
> - drop_id = cake_drop(sch, to_free);
> + prev_qlen = sch->q.qlen;
> + prev_backlog = sch->qstats.backlog;
>
> - if ((drop_id >> 16) == tin &&
> - (drop_id & 0xFFFF) == idx)
> - same_flow = true;
> - }
> - b->drop_overlimit += dropped;
> + while (q->buffer_used > q->buffer_limit) {
> + drop_id = cake_drop(sch, to_free);
> + if ((drop_id >> 16) == tin &&
> + (drop_id & 0xFFFF) == idx)
> + same_flow = true;
> + }
> +
> + /* Compute the droppped qlen and pkt length */
> + prev_qlen -= sch->q.qlen;
> + prev_backlog -= sch->qstats.backlog;
> + b->drop_overlimit += prev_backlog;
>
> - if (same_flow)
> - return NET_XMIT_CN;
> + if (same_flow) {
> + qdisc_tree_reduce_backlog(sch, prev_qlen - 1,
> + prev_backlog - len);
> + return NET_XMIT_CN;
> }
> + qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog);
> return NET_XMIT_SUCCESS;
> }
>
> --
> 2.43.0
>
Thanks Toke for the suggestions and explanations. The new version removes
redundant variable (dropped) and hanles the ack branch correctly.
Original PoC can't crash the patched version and the new patch passed the
self-test cases:
```log
ok 1 1212 - Create CAKE with default setting
ok 2 3281 - Create CAKE with bandwidth limit
ok 3 c940 - Create CAKE with autorate-ingress flag
ok 4 2310 - Create CAKE with rtt time
ok 5 2385 - Create CAKE with besteffort flag
ok 6 a032 - Create CAKE with diffserv8 flag
ok 7 2349 - Create CAKE with diffserv4 flag
ok 8 8472 - Create CAKE with flowblind flag
ok 9 2341 - Create CAKE with dsthost and nat flag
ok 10 5134 - Create CAKE with wash flag
ok 11 2302 - Create CAKE with flowblind and no-split-gso flag
ok 12 0768 - Create CAKE with dual-srchost and ack-filter flag
ok 13 0238 - Create CAKE with dual-dsthost and ack-filter-aggressive flag
ok 14 6572 - Create CAKE with memlimit and ptm flag
ok 15 2436 - Create CAKE with fwmark and atm flag
ok 16 3984 - Create CAKE with overhead and mpu
ok 17 5421 - Create CAKE with conservative and ingress flag
ok 18 6854 - Delete CAKE with conservative and ingress flag
ok 19 2342 - Replace CAKE with mpu
ok 20 2313 - Change CAKE with mpu
ok 21 4365 - Show CAKE class
```
Best,
Xiang
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-21 22:26 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-21 21:20 [Cake] [PATCH net v4] net/sched: sch_cake: Fix incorrect qlen reduction in cake_drop Xiang Mei
2025-11-21 21:26 ` [Cake] " Xiang Mei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox