net: sched: fix uses after free
syzbot reported one use-after-free in pfifo_fast_enqueue() [1]
Issue here is that we can not reuse skb after a successful skb_array_produce()
since another cpu might have consumed it already.
I believe a similar problem exists in try_bulk_dequeue_skb_slow()
in case we put an skb into qdisc_enqueue_skb_bad_txq() for lockless qdisc.
[1]
BUG: KASAN: use-after-free in qdisc_pkt_len include/net/sch_generic.h:610 [inline]
BUG: KASAN: use-after-free in qdisc_qstats_cpu_backlog_inc include/net/sch_generic.h:712 [inline]
BUG: KASAN: use-after-free in pfifo_fast_enqueue+0x4bc/0x5e0 net/sched/sch_generic.c:639
Read of size 4 at addr ffff8801cede37e8 by task syzkaller717588/5543
CPU: 1 PID: 5543 Comm: syzkaller717588 Not tainted 4.16.0-rc4+ #265
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:17 [inline]
dump_stack+0x194/0x24d lib/dump_stack.c:53
print_address_description+0x73/0x250 mm/kasan/report.c:256
kasan_report_error mm/kasan/report.c:354 [inline]
kasan_report+0x23c/0x360 mm/kasan/report.c:412
__asan_report_load4_noabort+0x14/0x20 mm/kasan/report.c:432
qdisc_pkt_len include/net/sch_generic.h:610 [inline]
qdisc_qstats_cpu_backlog_inc include/net/sch_generic.h:712 [inline]
pfifo_fast_enqueue+0x4bc/0x5e0 net/sched/sch_generic.c:639
__dev_xmit_skb net/core/dev.c:3216 [inline]
Fixes: c5ad119fb6
("net: sched: pfifo_fast use skb_array")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot+ed43b6903ab968b16f54@syzkaller.appspotmail.com
Cc: John Fastabend <john.fastabend@gmail.com>
Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Cc: Cong Wang <xiyou.wangcong@gmail.com>
Cc: Jiri Pirko <jiri@resnulli.us>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
7b7ef57c0d
commit
cce6294cc2
|
@ -106,6 +106,14 @@ static inline void qdisc_enqueue_skb_bad_txq(struct Qdisc *q,
|
|||
|
||||
__skb_queue_tail(&q->skb_bad_txq, skb);
|
||||
|
||||
if (qdisc_is_percpu_stats(q)) {
|
||||
qdisc_qstats_cpu_backlog_inc(q, skb);
|
||||
qdisc_qstats_cpu_qlen_inc(q);
|
||||
} else {
|
||||
qdisc_qstats_backlog_inc(q, skb);
|
||||
q->q.qlen++;
|
||||
}
|
||||
|
||||
if (lock)
|
||||
spin_unlock(lock);
|
||||
}
|
||||
|
@ -196,14 +204,6 @@ static void try_bulk_dequeue_skb_slow(struct Qdisc *q,
|
|||
break;
|
||||
if (unlikely(skb_get_queue_mapping(nskb) != mapping)) {
|
||||
qdisc_enqueue_skb_bad_txq(q, nskb);
|
||||
|
||||
if (qdisc_is_percpu_stats(q)) {
|
||||
qdisc_qstats_cpu_backlog_inc(q, nskb);
|
||||
qdisc_qstats_cpu_qlen_inc(q);
|
||||
} else {
|
||||
qdisc_qstats_backlog_inc(q, nskb);
|
||||
q->q.qlen++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
skb->next = nskb;
|
||||
|
@ -628,6 +628,7 @@ static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc *qdisc,
|
|||
int band = prio2band[skb->priority & TC_PRIO_MAX];
|
||||
struct pfifo_fast_priv *priv = qdisc_priv(qdisc);
|
||||
struct skb_array *q = band2list(priv, band);
|
||||
unsigned int pkt_len = qdisc_pkt_len(skb);
|
||||
int err;
|
||||
|
||||
err = skb_array_produce(q, skb);
|
||||
|
@ -636,7 +637,10 @@ static int pfifo_fast_enqueue(struct sk_buff *skb, struct Qdisc *qdisc,
|
|||
return qdisc_drop_cpu(skb, qdisc, to_free);
|
||||
|
||||
qdisc_qstats_cpu_qlen_inc(qdisc);
|
||||
qdisc_qstats_cpu_backlog_inc(qdisc, skb);
|
||||
/* Note: skb can not be used after skb_array_produce(),
|
||||
* so we better not use qdisc_qstats_cpu_backlog_inc()
|
||||
*/
|
||||
this_cpu_add(qdisc->cpu_qstats->backlog, pkt_len);
|
||||
return NET_XMIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue