diff options
author | Philip Pettersson <philip.pettersson@gmail.com> | 2016-11-30 14:55:36 -0800 |
---|---|---|
committer | Andreas Blaesius <skate4life@gmx.de> | 2017-03-17 11:02:08 +0100 |
commit | be5cd07d66823b3cde9ee7443d1c9dfdc0a8b070 (patch) | |
tree | d7dcd752dd9548f6273633ef9e356153d281abaa | |
parent | 69dc2da8e512692e0268ad1bc665472770e1e4cf (diff) | |
download | kernel_samsung_espresso10-be5cd07d66823b3cde9ee7443d1c9dfdc0a8b070.tar.gz kernel_samsung_espresso10-be5cd07d66823b3cde9ee7443d1c9dfdc0a8b070.tar.bz2 kernel_samsung_espresso10-be5cd07d66823b3cde9ee7443d1c9dfdc0a8b070.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; } |