Commit 9efd2329 authored by Toke Høiland-Jørgensen's avatar Toke Høiland-Jørgensen Committed by David S. Miller

sch_sfb: Don't assume the skb is still around after enqueueing to child

The sch_sfb enqueue() routine assumes the skb is still alive after it has
been enqueued into a child qdisc, using the data in the skb cb field in the
increment_qlen() routine after enqueue. However, the skb may in fact have
been freed, causing a use-after-free in this case. In particular, this
happens if sch_cake is used as a child of sfb, and the GSO splitting mode
of CAKE is enabled (in which case the skb will be split into segments and
the original skb freed).

Fix this by copying the sfb cb data to the stack before enqueueing the skb,
and using this stack copy in increment_qlen() instead of the skb pointer
itself.

Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-18231
Fixes: e13e02a3 ("net_sched: SFB flow scheduler")
Signed-off-by: default avatarToke Høiland-Jørgensen <toke@toke.dk>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7fdc7766
...@@ -135,15 +135,15 @@ static void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q) ...@@ -135,15 +135,15 @@ static void increment_one_qlen(u32 sfbhash, u32 slot, struct sfb_sched_data *q)
} }
} }
static void increment_qlen(const struct sk_buff *skb, struct sfb_sched_data *q) static void increment_qlen(const struct sfb_skb_cb *cb, struct sfb_sched_data *q)
{ {
u32 sfbhash; u32 sfbhash;
sfbhash = sfb_hash(skb, 0); sfbhash = cb->hashes[0];
if (sfbhash) if (sfbhash)
increment_one_qlen(sfbhash, 0, q); increment_one_qlen(sfbhash, 0, q);
sfbhash = sfb_hash(skb, 1); sfbhash = cb->hashes[1];
if (sfbhash) if (sfbhash)
increment_one_qlen(sfbhash, 1, q); increment_one_qlen(sfbhash, 1, q);
} }
...@@ -283,6 +283,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, ...@@ -283,6 +283,7 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
struct sfb_sched_data *q = qdisc_priv(sch); struct sfb_sched_data *q = qdisc_priv(sch);
struct Qdisc *child = q->qdisc; struct Qdisc *child = q->qdisc;
struct tcf_proto *fl; struct tcf_proto *fl;
struct sfb_skb_cb cb;
int i; int i;
u32 p_min = ~0; u32 p_min = ~0;
u32 minqlen = ~0; u32 minqlen = ~0;
...@@ -399,11 +400,12 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch, ...@@ -399,11 +400,12 @@ static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
} }
enqueue: enqueue:
memcpy(&cb, sfb_skb_cb(skb), sizeof(cb));
ret = qdisc_enqueue(skb, child, to_free); ret = qdisc_enqueue(skb, child, to_free);
if (likely(ret == NET_XMIT_SUCCESS)) { if (likely(ret == NET_XMIT_SUCCESS)) {
qdisc_qstats_backlog_inc(sch, skb); qdisc_qstats_backlog_inc(sch, skb);
sch->q.qlen++; sch->q.qlen++;
increment_qlen(skb, q); increment_qlen(&cb, q);
} else if (net_xmit_drop_count(ret)) { } else if (net_xmit_drop_count(ret)) {
q->stats.childdrop++; q->stats.childdrop++;
qdisc_qstats_drop(sch); qdisc_qstats_drop(sch);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment