Commit b29321b9 authored by Eric Dumazet's avatar Eric Dumazet Committed by Jiri Slaby

ipv6: lock socket in ip6_datagram_connect()

[ Upstream commit 03645a11 ]

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: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarJiri Slaby <jslaby@suse.cz>
parent 4a187391
...@@ -141,6 +141,7 @@ static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4) ...@@ -141,6 +141,7 @@ static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
} }
/* datagram.c */ /* datagram.c */
int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
extern int ip4_datagram_connect(struct sock *sk, extern int ip4_datagram_connect(struct sock *sk,
struct sockaddr *uaddr, int addr_len); struct sockaddr *uaddr, int addr_len);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <net/route.h> #include <net/route.h>
#include <net/tcp_states.h> #include <net/tcp_states.h>
int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr; 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,8 +39,6 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sk_dst_reset(sk); sk_dst_reset(sk);
lock_sock(sk);
oif = sk->sk_bound_dev_if; oif = sk->sk_bound_dev_if;
saddr = inet->inet_saddr; saddr = inet->inet_saddr;
if (ipv4_is_multicast(usin->sin_addr.s_addr)) { if (ipv4_is_multicast(usin->sin_addr.s_addr)) {
...@@ -81,9 +79,19 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -81,9 +79,19 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sk_dst_set(sk, &rt->dst); sk_dst_set(sk, &rt->dst);
err = 0; err = 0;
out: out:
release_sock(sk);
return err; return err;
} }
EXPORT_SYMBOL(__ip4_datagram_connect);
int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
int res;
lock_sock(sk);
res = __ip4_datagram_connect(sk, uaddr, addr_len);
release_sock(sk);
return res;
}
EXPORT_SYMBOL(ip4_datagram_connect); EXPORT_SYMBOL(ip4_datagram_connect);
/* Because UDP xmit path can manipulate sk_dst_cache without holding /* Because UDP xmit path can manipulate sk_dst_cache without holding
......
...@@ -40,7 +40,7 @@ static bool ipv6_mapped_addr_any(const struct in6_addr *a) ...@@ -40,7 +40,7 @@ static bool ipv6_mapped_addr_any(const struct in6_addr *a)
return ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0); return ipv6_addr_v4mapped(a) && (a->s6_addr32[3] == 0);
} }
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) static int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ {
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct inet_sock *inet = inet_sk(sk); 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,7 +56,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (usin->sin6_family == AF_INET) { if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk)) if (__ipv6_only_sock(sk))
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
err = ip4_datagram_connect(sk, uaddr, addr_len); err = __ip4_datagram_connect(sk, uaddr, addr_len);
goto ipv4_connected; goto ipv4_connected;
} }
...@@ -99,9 +99,9 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -99,9 +99,9 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sin.sin_addr.s_addr = daddr->s6_addr32[3]; sin.sin_addr.s_addr = daddr->s6_addr32[3];
sin.sin_port = usin->sin6_port; sin.sin_port = usin->sin6_port;
err = ip4_datagram_connect(sk, err = __ip4_datagram_connect(sk,
(struct sockaddr *) &sin, (struct sockaddr *) &sin,
sizeof(sin)); sizeof(sin));
ipv4_connected: ipv4_connected:
if (err) if (err)
...@@ -204,6 +204,16 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -204,6 +204,16 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
} }
int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
int res;
lock_sock(sk);
res = __ip6_datagram_connect(sk, uaddr, addr_len);
release_sock(sk);
return res;
}
EXPORT_SYMBOL_GPL(ip6_datagram_connect); EXPORT_SYMBOL_GPL(ip6_datagram_connect);
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment