Skip to content

Commit f6bab19

Browse files
tohojodavem330
authored andcommitted
sched: Avoid dereferencing skb pointer after child enqueue
Parent qdiscs may dereference the pointer to the enqueued skb after enqueue. However, both CAKE and TBF call consume_skb() on the original skb when splitting GSO packets, leading to a potential use-after-free in the parent. Fix this by avoiding dereferencing the skb pointer after enqueueing to the child. Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 80b3671 commit f6bab19

File tree

8 files changed

+23
-16
lines changed

8 files changed

+23
-16
lines changed

net/sched/sch_cbs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,14 @@ static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch,
8888
struct Qdisc *child,
8989
struct sk_buff **to_free)
9090
{
91+
unsigned int len = qdisc_pkt_len(skb);
9192
int err;
9293

9394
err = child->ops->enqueue(skb, child, to_free);
9495
if (err != NET_XMIT_SUCCESS)
9596
return err;
9697

97-
qdisc_qstats_backlog_inc(sch, skb);
98+
sch->qstats.backlog += len;
9899
sch->q.qlen++;
99100

100101
return NET_XMIT_SUCCESS;

net/sched/sch_drr.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ static struct drr_class *drr_classify(struct sk_buff *skb, struct Qdisc *sch,
350350
static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
351351
struct sk_buff **to_free)
352352
{
353+
unsigned int len = qdisc_pkt_len(skb);
353354
struct drr_sched *q = qdisc_priv(sch);
354355
struct drr_class *cl;
355356
int err = 0;
@@ -376,7 +377,7 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch,
376377
cl->deficit = cl->quantum;
377378
}
378379

379-
qdisc_qstats_backlog_inc(sch, skb);
380+
sch->qstats.backlog += len;
380381
sch->q.qlen++;
381382
return err;
382383
}

net/sched/sch_dsmark.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ static struct tcf_block *dsmark_tcf_block(struct Qdisc *sch, unsigned long cl,
199199
static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch,
200200
struct sk_buff **to_free)
201201
{
202+
unsigned int len = qdisc_pkt_len(skb);
202203
struct dsmark_qdisc_data *p = qdisc_priv(sch);
203204
int err;
204205

@@ -271,7 +272,7 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch,
271272
return err;
272273
}
273274

274-
qdisc_qstats_backlog_inc(sch, skb);
275+
sch->qstats.backlog += len;
275276
sch->q.qlen++;
276277

277278
return NET_XMIT_SUCCESS;

net/sched/sch_hfsc.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,7 @@ hfsc_dump_qdisc(struct Qdisc *sch, struct sk_buff *skb)
15391539
static int
15401540
hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
15411541
{
1542+
unsigned int len = qdisc_pkt_len(skb);
15421543
struct hfsc_class *cl;
15431544
int uninitialized_var(err);
15441545

@@ -1560,8 +1561,6 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
15601561
}
15611562

15621563
if (cl->qdisc->q.qlen == 1) {
1563-
unsigned int len = qdisc_pkt_len(skb);
1564-
15651564
if (cl->cl_flags & HFSC_RSC)
15661565
init_ed(cl, len);
15671566
if (cl->cl_flags & HFSC_FSC)
@@ -1576,7 +1575,7 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
15761575

15771576
}
15781577

1579-
qdisc_qstats_backlog_inc(sch, skb);
1578+
sch->qstats.backlog += len;
15801579
sch->q.qlen++;
15811580

15821581
return NET_XMIT_SUCCESS;

net/sched/sch_htb.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
581581
struct sk_buff **to_free)
582582
{
583583
int uninitialized_var(ret);
584+
unsigned int len = qdisc_pkt_len(skb);
584585
struct htb_sched *q = qdisc_priv(sch);
585586
struct htb_class *cl = htb_classify(skb, sch, &ret);
586587

@@ -610,7 +611,7 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
610611
htb_activate(q, cl);
611612
}
612613

613-
qdisc_qstats_backlog_inc(sch, skb);
614+
sch->qstats.backlog += len;
614615
sch->q.qlen++;
615616
return NET_XMIT_SUCCESS;
616617
}

net/sched/sch_prio.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
7272
static int
7373
prio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
7474
{
75+
unsigned int len = qdisc_pkt_len(skb);
7576
struct Qdisc *qdisc;
7677
int ret;
7778

@@ -88,7 +89,7 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free)
8889

8990
ret = qdisc_enqueue(skb, qdisc, to_free);
9091
if (ret == NET_XMIT_SUCCESS) {
91-
qdisc_qstats_backlog_inc(sch, skb);
92+
sch->qstats.backlog += len;
9293
sch->q.qlen++;
9394
return NET_XMIT_SUCCESS;
9495
}

net/sched/sch_qfq.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,7 @@ static struct qfq_aggregate *qfq_choose_next_agg(struct qfq_sched *q)
12101210
static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12111211
struct sk_buff **to_free)
12121212
{
1213+
unsigned int len = qdisc_pkt_len(skb), gso_segs;
12131214
struct qfq_sched *q = qdisc_priv(sch);
12141215
struct qfq_class *cl;
12151216
struct qfq_aggregate *agg;
@@ -1224,17 +1225,17 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12241225
}
12251226
pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid);
12261227

1227-
if (unlikely(cl->agg->lmax < qdisc_pkt_len(skb))) {
1228+
if (unlikely(cl->agg->lmax < len)) {
12281229
pr_debug("qfq: increasing maxpkt from %u to %u for class %u",
1229-
cl->agg->lmax, qdisc_pkt_len(skb), cl->common.classid);
1230-
err = qfq_change_agg(sch, cl, cl->agg->class_weight,
1231-
qdisc_pkt_len(skb));
1230+
cl->agg->lmax, len, cl->common.classid);
1231+
err = qfq_change_agg(sch, cl, cl->agg->class_weight, len);
12321232
if (err) {
12331233
cl->qstats.drops++;
12341234
return qdisc_drop(skb, sch, to_free);
12351235
}
12361236
}
12371237

1238+
gso_segs = skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
12381239
err = qdisc_enqueue(skb, cl->qdisc, to_free);
12391240
if (unlikely(err != NET_XMIT_SUCCESS)) {
12401241
pr_debug("qfq_enqueue: enqueue failed %d\n", err);
@@ -1245,16 +1246,17 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
12451246
return err;
12461247
}
12471248

1248-
bstats_update(&cl->bstats, skb);
1249-
qdisc_qstats_backlog_inc(sch, skb);
1249+
cl->bstats.bytes += len;
1250+
cl->bstats.packets += gso_segs;
1251+
sch->qstats.backlog += len;
12501252
++sch->q.qlen;
12511253

12521254
agg = cl->agg;
12531255
/* if the queue was not empty, then done here */
12541256
if (cl->qdisc->q.qlen != 1) {
12551257
if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) &&
12561258
list_first_entry(&agg->active, struct qfq_class, alist)
1257-
== cl && cl->deficit < qdisc_pkt_len(skb))
1259+
== cl && cl->deficit < len)
12581260
list_move_tail(&cl->alist, &agg->active);
12591261

12601262
return err;

net/sched/sch_tbf.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch,
185185
struct sk_buff **to_free)
186186
{
187187
struct tbf_sched_data *q = qdisc_priv(sch);
188+
unsigned int len = qdisc_pkt_len(skb);
188189
int ret;
189190

190191
if (qdisc_pkt_len(skb) > q->max_size) {
@@ -200,7 +201,7 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch,
200201
return ret;
201202
}
202203

203-
qdisc_qstats_backlog_inc(sch, skb);
204+
sch->qstats.backlog += len;
204205
sch->q.qlen++;
205206
return NET_XMIT_SUCCESS;
206207
}

0 commit comments

Comments
 (0)