Skip to content

Commit

Permalink
ipv6: lock socket in ip6_datagram_connect()
Browse files Browse the repository at this point in the history
[ Upstream commit 03645a1 ]

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
  • Loading branch information
Eric Dumazet authored and sashalevin committed Apr 28, 2016
1 parent f28e268 commit 41550c7
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 9 deletions.
1 change: 1 addition & 0 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ static inline __u8 get_rtconn_flags(struct ipcm_cookie* ipc, struct sock* sk)
}

/* datagram.c */
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);

void ip4_datagram_release_cb(struct sock *sk);
Expand Down
16 changes: 12 additions & 4 deletions net/ipv4/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#include <net/route.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 sockaddr_in *usin = (struct sockaddr_in *) uaddr;
Expand All @@ -39,8 +39,6 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)

sk_dst_reset(sk);

lock_sock(sk);

oif = sk->sk_bound_dev_if;
saddr = inet->inet_saddr;
if (ipv4_is_multicast(usin->sin_addr.s_addr)) {
Expand Down Expand Up @@ -81,9 +79,19 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sk_dst_set(sk, &rt->dst);
err = 0;
out:
release_sock(sk);
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);

/* Because UDP xmit path can manipulate sk_dst_cache without holding
Expand Down
20 changes: 15 additions & 5 deletions net/ipv6/datagram.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

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 inet_sock *inet = inet_sk(sk);
Expand All @@ -56,7 +56,7 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (usin->sin6_family == AF_INET) {
if (__ipv6_only_sock(sk))
return -EAFNOSUPPORT;
err = ip4_datagram_connect(sk, uaddr, addr_len);
err = __ip4_datagram_connect(sk, uaddr, addr_len);
goto ipv4_connected;
}

Expand Down Expand Up @@ -98,9 +98,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_port = usin->sin6_port;

err = ip4_datagram_connect(sk,
(struct sockaddr *) &sin,
sizeof(sin));
err = __ip4_datagram_connect(sk,
(struct sockaddr *) &sin,
sizeof(sin));

ipv4_connected:
if (err)
Expand Down Expand Up @@ -203,6 +203,16 @@ int ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
fl6_sock_release(flowlabel);
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);

int ip6_datagram_connect_v6_only(struct sock *sk, struct sockaddr *uaddr,
Expand Down

0 comments on commit 41550c7

Please sign in to comment.