diff options
author | Philip Pettersson <philip.pettersson@gmail.com> | 2016-11-30 14:55:36 -0800 |
---|---|---|
committer | Andreas Blaesius <skate4life@gmx.de> | 2017-05-17 19:25:41 +0200 |
commit | 68de1a70fe33cae50f6ec4e212f00a574e1873b6 (patch) | |
tree | 2ea34ca415f8c020a70be5a871118afcbb42e7ea | |
parent | e85271027a5a30442fd4a383e483810d5cdbea3b (diff) | |
download | kernel_samsung_tuna-68de1a70fe33cae50f6ec4e212f00a574e1873b6.tar.gz kernel_samsung_tuna-68de1a70fe33cae50f6ec4e212f00a574e1873b6.tar.bz2 kernel_samsung_tuna-68de1a70fe33cae50f6ec4e212f00a574e1873b6.zip |
packet: fix race condition in packet_set_ring
When packet_set_ring creates a ring buffer it will initialize a
struct timer_list if the packet version is TPACKET_V3. This value
can then be raced by a different thread calling setsockopt to
set the version to TPACKET_V1 before packet_set_ring has finished.
This leads to a use-after-free on a function pointer in the
struct timer_list when the socket is closed as the previously
initialized timer will not be deleted.
The bug is fixed by taking lock_sock(sk) in packet_setsockopt when
changing the packet version while also taking the lock at the start
of packet_set_ring.
Change-Id: Iec8b20f499134e1edd0f9214aa4dde477d1674e1
Fixes: f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation.")
Signed-off-by: Philip Pettersson <philip.pettersson@gmail.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/packet/af_packet.c | 21 |
1 files changed, 13 insertions, 8 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d596ceb967f..d0c3cd7dd12 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2014,18 +2014,24 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv if (optlen != sizeof(val)) return -EINVAL; - if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) - return -EBUSY; if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT; switch (val) { case TPACKET_V1: case TPACKET_V2: - po->tp_version = val; - return 0; + break; default: return -EINVAL; } + lock_sock(sk); + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { + ret = -EBUSY; + } else { + po->tp_version = val; + ret = 0; + } + release_sock(sk); + return ret; } case PACKET_RESERVE: { @@ -2463,6 +2469,8 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, rb = tx_ring ? &po->tx_ring : &po->rx_ring; rb_queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue; + lock_sock(sk); + err = -EBUSY; if (!closing) { if (atomic_read(&po->mapped)) @@ -2517,8 +2525,6 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, goto out; } - lock_sock(sk); - /* Detach socket from network */ spin_lock(&po->bind_lock); was_running = po->running; @@ -2566,11 +2572,10 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, } spin_unlock(&po->bind_lock); - release_sock(sk); - if (pg_vec) free_pg_vec(pg_vec, order, req->tp_block_nr); out: + release_sock(sk); return err; } |