| From: Eric Dumazet <edumazet@google.com> |
| Date: Tue, 14 Feb 2017 09:03:51 -0800 |
| Subject: packet: fix races in fanout_add() |
| |
| commit d199fab63c11998a602205f7ee7ff7c05c97164b upstream. |
| |
| Multiple threads can call fanout_add() at the same time. |
| |
| We need to grab fanout_mutex earlier to avoid races that could |
| lead to one thread freeing po->rollover that was set by another thread. |
| |
| Do the same in fanout_release(), for peace of mind, and to help us |
| finding lockdep issues earlier. |
| |
| Fixes: dc99f600698d ("packet: Add fanout support.") |
| Fixes: 0648ab70afe6 ("packet: rollover prepare: per-socket state") |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Cc: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| [bwh: Backported to 3.16: |
| - No rollover queue stats |
| - Adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| --- a/net/packet/af_packet.c |
| +++ b/net/packet/af_packet.c |
| @@ -1451,13 +1451,16 @@ static int fanout_add(struct sock *sk, u |
| return -EINVAL; |
| } |
| |
| + mutex_lock(&fanout_mutex); |
| + |
| + err = -EINVAL; |
| if (!po->running) |
| - return -EINVAL; |
| + goto out; |
| |
| + err = -EALREADY; |
| if (po->fanout) |
| - return -EALREADY; |
| + goto out; |
| |
| - mutex_lock(&fanout_mutex); |
| match = NULL; |
| list_for_each_entry(f, &fanout_list, list) { |
| if (f->id == id && |
| @@ -1513,17 +1516,16 @@ static void fanout_release(struct sock * |
| struct packet_sock *po = pkt_sk(sk); |
| struct packet_fanout *f; |
| |
| - f = po->fanout; |
| - if (!f) |
| - return; |
| - |
| mutex_lock(&fanout_mutex); |
| - po->fanout = NULL; |
| + f = po->fanout; |
| + if (f) { |
| + po->fanout = NULL; |
| |
| - if (atomic_dec_and_test(&f->sk_ref)) { |
| - list_del(&f->list); |
| - dev_remove_pack(&f->prot_hook); |
| - kfree(f); |
| + if (atomic_dec_and_test(&f->sk_ref)) { |
| + list_del(&f->list); |
| + dev_remove_pack(&f->prot_hook); |
| + kfree(f); |
| + } |
| } |
| mutex_unlock(&fanout_mutex); |
| } |