ipv6: kill sk_dst_lock
While testing the np->opt RCU conversion, I found that UDP/IPv6 was using a mixture of xchg() and sk_dst_lock to protect concurrent changes to sk->sk_dst_cache, leading to possible corruptions and crashes. ip6_sk_dst_lookup_flow() uses sk_dst_check() anyway, so the simplest way to fix the mess is to remove sk_dst_lock completely, as we did for IPv4. __ip6_dst_store() and ip6_dst_store() share same implementation. sk_setup_caps() being called with socket lock being held or not, we have to use sk_dst_set() instead of __sk_dst_set() Note that I had to move the "np->dst_cookie = rt6_get_cookie(rt);" in ip6_dst_store() before the sk_setup_caps(sk, dst) call. This is because ip6_dst_store() can be called from process context, without any lock held. As soon as the dst is installed in sk->sk_dst_cache, dst can be freed from another cpu doing a concurrent ip6_dst_store() Doing the dst dereference before doing the install is needed to make sure no use after free would trigger. Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
c836a8ba93
commit
6bd4f355df
@@ -133,27 +133,18 @@ void rt6_clean_tohost(struct net *net, struct in6_addr *gateway);
|
||||
/*
|
||||
* Store a destination cache entry in a socket
|
||||
*/
|
||||
static inline void __ip6_dst_store(struct sock *sk, struct dst_entry *dst,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
{
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct rt6_info *rt = (struct rt6_info *) dst;
|
||||
|
||||
np->dst_cookie = rt6_get_cookie((struct rt6_info *)dst);
|
||||
sk_setup_caps(sk, dst);
|
||||
np->daddr_cache = daddr;
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
np->saddr_cache = saddr;
|
||||
#endif
|
||||
np->dst_cookie = rt6_get_cookie(rt);
|
||||
}
|
||||
|
||||
static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst,
|
||||
struct in6_addr *daddr, struct in6_addr *saddr)
|
||||
{
|
||||
spin_lock(&sk->sk_dst_lock);
|
||||
__ip6_dst_store(sk, dst, daddr, saddr);
|
||||
spin_unlock(&sk->sk_dst_lock);
|
||||
}
|
||||
|
||||
static inline bool ipv6_unicast_destination(const struct sk_buff *skb)
|
||||
|
Reference in New Issue
Block a user