Skip to content

Commit 2d3cbfd

Browse files
congwangdavem330
authored andcommitted
net_sched: Flush gso_skb list too during ->change()
Previously, when reducing a qdisc's limit via the ->change() operation, only the main skb queue was trimmed, potentially leaving packets in the gso_skb list. This could result in NULL pointer dereference when we only check sch->limit against sch->q.qlen. This patch introduces a new helper, qdisc_dequeue_internal(), which ensures both the gso_skb list and the main queue are properly flushed when trimming excess packets. All relevant qdiscs (codel, fq, fq_codel, fq_pie, hhf, pie) are updated to use this helper in their ->change() routines. Fixes: 76e3cc1 ("codel: Controlled Delay AQM") Fixes: 4b549a2 ("fq_codel: Fair Queue Codel AQM") Fixes: afe4fd0 ("pkt_sched: fq: Fair Queue packet scheduler") Fixes: ec97ecf ("net: sched: add Flow Queue PIE packet scheduler") Fixes: 10239ed ("net-qdisc-hhf: Heavy-Hitter Filter (HHF) qdisc") Fixes: d4b3621 ("net: pkt_sched: PIE AQM scheme") Reported-by: Will <[email protected]> Reported-by: Savy <[email protected]> Signed-off-by: Cong Wang <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 6b3ab7f commit 2d3cbfd

File tree

7 files changed

+21
-6
lines changed

7 files changed

+21
-6
lines changed

include/net/sch_generic.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,21 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct qdisc_skb_head *qh)
10311031
return skb;
10321032
}
10331033

1034+
static inline struct sk_buff *qdisc_dequeue_internal(struct Qdisc *sch, bool direct)
1035+
{
1036+
struct sk_buff *skb;
1037+
1038+
skb = __skb_dequeue(&sch->gso_skb);
1039+
if (skb) {
1040+
sch->q.qlen--;
1041+
return skb;
1042+
}
1043+
if (direct)
1044+
return __qdisc_dequeue_head(&sch->q);
1045+
else
1046+
return sch->dequeue(sch);
1047+
}
1048+
10341049
static inline struct sk_buff *qdisc_dequeue_head(struct Qdisc *sch)
10351050
{
10361051
struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);

net/sched/sch_codel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt,
144144

145145
qlen = sch->q.qlen;
146146
while (sch->q.qlen > sch->limit) {
147-
struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);
147+
struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
148148

149149
dropped += qdisc_pkt_len(skb);
150150
qdisc_qstats_backlog_dec(sch, skb);

net/sched/sch_fq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1136,7 +1136,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt,
11361136
sch_tree_lock(sch);
11371137
}
11381138
while (sch->q.qlen > sch->limit) {
1139-
struct sk_buff *skb = fq_dequeue(sch);
1139+
struct sk_buff *skb = qdisc_dequeue_internal(sch, false);
11401140

11411141
if (!skb)
11421142
break;

net/sched/sch_fq_codel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt,
441441

442442
while (sch->q.qlen > sch->limit ||
443443
q->memory_usage > q->memory_limit) {
444-
struct sk_buff *skb = fq_codel_dequeue(sch);
444+
struct sk_buff *skb = qdisc_dequeue_internal(sch, false);
445445

446446
q->cstats.drop_len += qdisc_pkt_len(skb);
447447
rtnl_kfree_skbs(skb, skb);

net/sched/sch_fq_pie.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt,
366366

367367
/* Drop excess packets if new limit is lower */
368368
while (sch->q.qlen > sch->limit) {
369-
struct sk_buff *skb = fq_pie_qdisc_dequeue(sch);
369+
struct sk_buff *skb = qdisc_dequeue_internal(sch, false);
370370

371371
len_dropped += qdisc_pkt_len(skb);
372372
num_dropped += 1;

net/sched/sch_hhf.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ static int hhf_change(struct Qdisc *sch, struct nlattr *opt,
564564
qlen = sch->q.qlen;
565565
prev_backlog = sch->qstats.backlog;
566566
while (sch->q.qlen > sch->limit) {
567-
struct sk_buff *skb = hhf_dequeue(sch);
567+
struct sk_buff *skb = qdisc_dequeue_internal(sch, false);
568568

569569
rtnl_kfree_skbs(skb, skb);
570570
}

net/sched/sch_pie.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ static int pie_change(struct Qdisc *sch, struct nlattr *opt,
195195
/* Drop excess packets if new limit is lower */
196196
qlen = sch->q.qlen;
197197
while (sch->q.qlen > sch->limit) {
198-
struct sk_buff *skb = __qdisc_dequeue_head(&sch->q);
198+
struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
199199

200200
dropped += qdisc_pkt_len(skb);
201201
qdisc_qstats_backlog_dec(sch, skb);

0 commit comments

Comments
 (0)