mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
ipv6: lockless IPV6_UNICAST_HOPS implementation
Some np->hop_limit accesses are racy, when socket lock is not held. Add missing annotations and switch to full lockless implementation. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
f2fa1c812c
commit
b0adfba7ee
@ -213,17 +213,7 @@ struct ipv6_pinfo {
|
||||
__be32 flow_label;
|
||||
__u32 frag_size;
|
||||
|
||||
/*
|
||||
* Packed in 16bits.
|
||||
* Omit one shift by putting the signed field at MSB.
|
||||
*/
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
__s16 hop_limit:9;
|
||||
__u16 __unused_1:7;
|
||||
#else
|
||||
__u16 __unused_1:7;
|
||||
__s16 hop_limit:9;
|
||||
#endif
|
||||
s16 hop_limit;
|
||||
|
||||
#if defined(__BIG_ENDIAN_BITFIELD)
|
||||
/* Packed in 16bits. */
|
||||
|
@ -916,7 +916,7 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
|
||||
if (ipv6_addr_is_multicast(&fl6->daddr))
|
||||
hlimit = np->mcast_hops;
|
||||
else
|
||||
hlimit = np->hop_limit;
|
||||
hlimit = READ_ONCE(np->hop_limit);
|
||||
if (hlimit < 0)
|
||||
hlimit = ip6_dst_hoplimit(dst);
|
||||
return hlimit;
|
||||
|
@ -309,7 +309,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
|
||||
* Fill in the IPv6 header
|
||||
*/
|
||||
if (np)
|
||||
hlimit = np->hop_limit;
|
||||
hlimit = READ_ONCE(np->hop_limit);
|
||||
if (hlimit < 0)
|
||||
hlimit = ip6_dst_hoplimit(dst);
|
||||
|
||||
|
@ -415,6 +415,16 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
if (ip6_mroute_opt(optname))
|
||||
return ip6_mroute_setsockopt(sk, optname, optval, optlen);
|
||||
|
||||
/* Handle options that can be set without locking the socket. */
|
||||
switch (optname) {
|
||||
case IPV6_UNICAST_HOPS:
|
||||
if (optlen < sizeof(int))
|
||||
return -EINVAL;
|
||||
if (val > 255 || val < -1)
|
||||
return -EINVAL;
|
||||
WRITE_ONCE(np->hop_limit, val);
|
||||
return 0;
|
||||
}
|
||||
if (needs_rtnl)
|
||||
rtnl_lock();
|
||||
sockopt_lock_sock(sk);
|
||||
@ -733,14 +743,6 @@ done:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IPV6_UNICAST_HOPS:
|
||||
if (optlen < sizeof(int))
|
||||
goto e_inval;
|
||||
if (val > 255 || val < -1)
|
||||
goto e_inval;
|
||||
np->hop_limit = val;
|
||||
retv = 0;
|
||||
break;
|
||||
|
||||
case IPV6_MULTICAST_HOPS:
|
||||
if (sk->sk_type == SOCK_STREAM)
|
||||
@ -1347,7 +1349,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
struct dst_entry *dst;
|
||||
|
||||
if (optname == IPV6_UNICAST_HOPS)
|
||||
val = np->hop_limit;
|
||||
val = READ_ONCE(np->hop_limit);
|
||||
else
|
||||
val = np->mcast_hops;
|
||||
|
||||
|
@ -1716,7 +1716,7 @@ static void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb,
|
||||
|
||||
hdr->payload_len = htons(len);
|
||||
hdr->nexthdr = proto;
|
||||
hdr->hop_limit = inet6_sk(sk)->hop_limit;
|
||||
hdr->hop_limit = READ_ONCE(inet6_sk(sk)->hop_limit);
|
||||
|
||||
hdr->saddr = *saddr;
|
||||
hdr->daddr = *daddr;
|
||||
|
@ -500,7 +500,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
|
||||
csum_partial(icmp6h,
|
||||
skb->len, 0));
|
||||
|
||||
ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
|
||||
ip6_nd_hdr(skb, saddr, daddr, READ_ONCE(inet6_sk(sk)->hop_limit), skb->len);
|
||||
|
||||
rcu_read_lock();
|
||||
idev = __in6_dev_get(dst->dev);
|
||||
|
Loading…
Reference in New Issue
Block a user