diff options
author | Eric Dumazet <edumazet@google.com> | 2015-07-14 02:10:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-07-15 20:25:51 -0400 |
commit | 03645a11a570d52e70631838cb786eb4253eb463 (patch) | |
tree | 8a370b57010d040b59fd7e17ba90d1d8ed1c7650 | |
parent | 14e4cec80cd05ad585c537db56a705ab61fbaff9 (diff) |
ipv6: lock socket in ip6_datagram_connect()
ip6_datagram_connect() is doing a lot of socket changes without
socket being locked.
This looks wrong, at least for udp_lib_rehash() which could corrupt
lists because of concurrent udp_sk(sk)->udp_portaddr_hash accesses.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/ip.h | 1 | ||||
-rw-r--r-- | net/ipv4/datagram.c | 16 | ||||
-rw-r--r-- | net/ipv6/datagram.c | 20 |
3 files changed, 28 insertions, 9 deletions
diff --git a/include/net/ip.h b/include/net/ip.h index 0750a186ea63..d5fe9f2ab699 100644 --- a/include/net/ip.h +++ b/include/net/ip.h | |||
@@ -161,6 +161,7 @@ static inline __u8 get_rtconn_flags(struct ipcm_cookie* ipc, struct sock* sk) | |||
161 | } | 161 | } |
162 | 162 | ||
163 | /* datagram.c */ | 163 | /* datagram.c */ |
164 | int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); | ||
164 | int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); | 165 | int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); |
165 | 166 | ||
166 | void ip4_datagram_release_cb(struct sock *sk); | 167 | void ip4_datagram_release_cb(struct sock *sk); |
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 90c0e8386116..574fad9cca05 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <net/route.h> | 20 | #include <net/route.h> |
21 | #include <net/tcp_states.h> | 21 | #include <net/tcp_states.h> |
22 | 22 | ||
23 | int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | 23 | int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
24 | { | 24 | { |
25 | struct inet_sock *inet = inet_sk(sk); | 25 | struct inet_sock *inet = inet_sk(sk); |
26 | struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; | 26 | struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; |
@@ -39,8 +39,6 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
39 | 39 | ||
40 | sk_dst_reset(sk); | 40 | sk_dst_reset(sk); |
41 | 41 | ||
42 | lock_sock(sk); | ||
43 | |||
44 | oif = sk->sk_bound_dev_if; | 42 | oif = sk->sk_bound_dev_if; |
45 | saddr = inet->inet_saddr; | 43 | saddr = inet->inet_saddr; |
46 | if (ipv4_is_multicast(usin->sin_addr.s_addr)) { | 44 | if (ipv4_is_multicast(usin->sin_addr.s_addr)) { |
@@ -82,9 +80,19 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
82 | sk_dst_set(sk, &rt->dst); | 80 | sk_dst_set(sk, &rt->dst); |
83 | err = 0; | 81 | err = 0; |
84 | out: | 82 | out: |
85 | release_sock(sk); | ||
86 | return err; | 83 | return err; |
87 | } | 84 | } |
85 | EXPORT_SYMBOL(__ip4_datagram_connect); | ||
86 | |||
87 | int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||
88 | { | ||
89 | int res; | ||
90 | |||
91 | lock_sock(sk); | ||
92 | res = __ip4_datagram_connect(sk, uaddr, addr_len); | ||
93 | release_sock(sk); | ||
94 | return res; | ||
95 | } | ||
88 | EXPORT_SYMBOL(ip4_datagram_connect); | 96 | EXPORT_SYMBOL(ip4_datagram_connect); |
89 | 97 | ||
90 | /* Because UDP xmit path can manipulate sk_dst_cache without holding | 98 | /* Because UDP xmit path can manipulate sk_dst_cache without holding |
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 62d908e64eeb..b10a88986a98 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c | |||
@@ -40,7 +40,7 @@ static bool ipv6_mapped_addr_any(const struct in6_addr *a) | |||
40 | return ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0); | 40 | return ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0); |
41 | } | 41 | } |
42 | 42 | ||
43 | int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | 43 | static int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) |
44 | { | 44 | { |
45 | struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; | 45 | struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; |
46 | struct inet_sock *inet = inet_sk(sk); | 46 | struct inet_sock *inet = inet_sk(sk); |
@@ -56,7 +56,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
56 | if (usin->sin6_family == AF_INET) { | 56 | if (usin->sin6_family == AF_INET) { |
57 | if (__ipv6_only_sock(sk)) | 57 | if (__ipv6_only_sock(sk)) |
58 | return -EAFNOSUPPORT; | 58 | return -EAFNOSUPPORT; |
59 | err = ip4_datagram_connect(sk, uaddr, addr_len); | 59 | err = __ip4_datagram_connect(sk, uaddr, addr_len); |
60 | goto ipv4_connected; | 60 | goto ipv4_connected; |
61 | } | 61 | } |
62 | 62 | ||
@@ -98,9 +98,9 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | |||
98 | sin.sin_addr.s_addr = daddr->s6_addr32[3]; | 98 | sin.sin_addr.s_addr = daddr->s6_addr32[3]; |
99 | sin.sin_port = usin->sin6_port; | 99 | sin.sin_port = usin->sin6_port; |
100 | 100 | ||
101 | err = ip4_datagram_connect(sk, | 101 | err = __ip4_datagram_connect(sk, |
102 | (struct sockaddr *) &sin, | 102 | (struct sockaddr *) &sin, |
103 | sizeof(sin)); | 103 | sizeof(sin)); |
104 | 104 | ||
105 | ipv4_connected: | 105 | ipv4_connected: |
106 | if (err) | 106 | if (err) |
@@ -204,6 +204,16 @@ out: | |||
204 | fl6_sock_release(flowlabel); | 204 | fl6_sock_release(flowlabel); |
205 | return err; | 205 | return err; |
206 | } | 206 | } |
207 | |||
208 | int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) | ||
209 | { | ||
210 | int res; | ||
211 | |||
212 | lock_sock(sk); | ||
213 | res = __ip6_datagram_connect(sk, uaddr, addr_len); | ||
214 | release_sock(sk); | ||
215 | return res; | ||
216 | } | ||
207 | EXPORT_SYMBOL_GPL(ip6_datagram_connect); | 217 | EXPORT_SYMBOL_GPL(ip6_datagram_connect); |
208 | 218 | ||
209 | int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr, | 219 | int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr, |