]>
Commit | Line | Data |
---|---|---|
527d75c3 FG |
1 | From 84ac7260236a49c79eede91617700174c2c19b0c Mon Sep 17 00:00:00 2001 |
2 | From: Philip Pettersson <philip.pettersson@gmail.com> | |
3 | Date: Wed, 30 Nov 2016 14:55:36 -0800 | |
4 | Subject: packet: fix race condition in packet_set_ring | |
5 | ||
6 | When packet_set_ring creates a ring buffer it will initialize a | |
7 | struct timer_list if the packet version is TPACKET_V3. This value | |
8 | can then be raced by a different thread calling setsockopt to | |
9 | set the version to TPACKET_V1 before packet_set_ring has finished. | |
10 | ||
11 | This leads to a use-after-free on a function pointer in the | |
12 | struct timer_list when the socket is closed as the previously | |
13 | initialized timer will not be deleted. | |
14 | ||
15 | The bug is fixed by taking lock_sock(sk) in packet_setsockopt when | |
16 | changing the packet version while also taking the lock at the start | |
17 | of packet_set_ring. | |
18 | ||
19 | Fixes: f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation.") | |
20 | Signed-off-by: Philip Pettersson <philip.pettersson@gmail.com> | |
21 | Signed-off-by: Eric Dumazet <edumazet@google.com> | |
22 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
23 | --- | |
24 | net/packet/af_packet.c | 18 ++++++++++++------ | |
25 | 1 file changed, 12 insertions(+), 6 deletions(-) | |
26 | ||
27 | diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c | |
28 | index d2238b2..dd23323 100644 | |
29 | --- a/net/packet/af_packet.c | |
30 | +++ b/net/packet/af_packet.c | |
31 | @@ -3648,19 +3648,25 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv | |
32 | ||
33 | if (optlen != sizeof(val)) | |
34 | return -EINVAL; | |
35 | - if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) | |
36 | - return -EBUSY; | |
37 | if (copy_from_user(&val, optval, sizeof(val))) | |
38 | return -EFAULT; | |
39 | switch (val) { | |
40 | case TPACKET_V1: | |
41 | case TPACKET_V2: | |
42 | case TPACKET_V3: | |
43 | - po->tp_version = val; | |
44 | - return 0; | |
45 | + break; | |
46 | default: | |
47 | return -EINVAL; | |
48 | } | |
49 | + lock_sock(sk); | |
50 | + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { | |
51 | + ret = -EBUSY; | |
52 | + } else { | |
53 | + po->tp_version = val; | |
54 | + ret = 0; | |
55 | + } | |
56 | + release_sock(sk); | |
57 | + return ret; | |
58 | } | |
59 | case PACKET_RESERVE: | |
60 | { | |
61 | @@ -4164,6 +4170,7 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, | |
62 | /* Added to avoid minimal code churn */ | |
63 | struct tpacket_req *req = &req_u->req; | |
64 | ||
65 | + lock_sock(sk); | |
66 | /* Opening a Tx-ring is NOT supported in TPACKET_V3 */ | |
67 | if (!closing && tx_ring && (po->tp_version > TPACKET_V2)) { | |
68 | net_warn_ratelimited("Tx-ring is not supported.\n"); | |
69 | @@ -4245,7 +4252,6 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, | |
70 | goto out; | |
71 | } | |
72 | ||
73 | - lock_sock(sk); | |
74 | ||
75 | /* Detach socket from network */ | |
76 | spin_lock(&po->bind_lock); | |
77 | @@ -4294,11 +4300,11 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, | |
78 | if (!tx_ring) | |
79 | prb_shutdown_retire_blk_timer(po, rb_queue); | |
80 | } | |
81 | - release_sock(sk); | |
82 | ||
83 | if (pg_vec) | |
84 | free_pg_vec(pg_vec, order, req->tp_block_nr); | |
85 | out: | |
86 | + release_sock(sk); | |
87 | return err; | |
88 | } | |
89 | ||
90 | -- | |
91 | cgit v0.12 | |
92 |