mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
ipv6: annotate data-races around np->mcast_oif
np->mcast_oif is read locklessly in some contexts. Make all accesses to this field lockless, adding appropriate annotations. This also makes setsockopt( IPV6_MULTICAST_IF ) lockless. 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
9a64d4c93e
commit
d2f011a0bf
@ -669,7 +669,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
ipv6_pktoptions:
|
||||
if (!((1 << sk->sk_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) {
|
||||
if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
|
||||
np->mcast_oif = inet6_iif(opt_skb);
|
||||
WRITE_ONCE(np->mcast_oif, inet6_iif(opt_skb));
|
||||
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
|
||||
WRITE_ONCE(np->mcast_hops, ipv6_hdr(opt_skb)->hop_limit);
|
||||
if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass)
|
||||
|
@ -60,7 +60,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6,
|
||||
|
||||
if (!oif) {
|
||||
if (ipv6_addr_is_multicast(&fl6->daddr))
|
||||
oif = np->mcast_oif;
|
||||
oif = READ_ONCE(np->mcast_oif);
|
||||
else
|
||||
oif = np->ucast_oif;
|
||||
}
|
||||
@ -229,7 +229,7 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
|
||||
}
|
||||
|
||||
if (!sk->sk_bound_dev_if && (addr_type & IPV6_ADDR_MULTICAST))
|
||||
WRITE_ONCE(sk->sk_bound_dev_if, np->mcast_oif);
|
||||
WRITE_ONCE(sk->sk_bound_dev_if, READ_ONCE(np->mcast_oif));
|
||||
|
||||
/* Connect to link-local address requires an interface */
|
||||
if (!sk->sk_bound_dev_if) {
|
||||
|
@ -584,7 +584,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
|
||||
tmp_hdr.icmp6_pointer = htonl(info);
|
||||
|
||||
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
|
||||
fl6.flowi6_oif = np->mcast_oif;
|
||||
fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!fl6.flowi6_oif)
|
||||
fl6.flowi6_oif = np->ucast_oif;
|
||||
|
||||
@ -770,7 +770,7 @@ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
|
||||
np = inet6_sk(sk);
|
||||
|
||||
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
|
||||
fl6.flowi6_oif = np->mcast_oif;
|
||||
fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!fl6.flowi6_oif)
|
||||
fl6.flowi6_oif = np->ucast_oif;
|
||||
|
||||
|
@ -509,6 +509,34 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
if (optlen < sizeof(int))
|
||||
return -EINVAL;
|
||||
return ip6_sock_set_addr_preferences(sk, val);
|
||||
case IPV6_MULTICAST_IF:
|
||||
if (sk->sk_type == SOCK_STREAM)
|
||||
return -ENOPROTOOPT;
|
||||
if (optlen < sizeof(int))
|
||||
return -EINVAL;
|
||||
if (val) {
|
||||
struct net_device *dev;
|
||||
int bound_dev_if, midx;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
dev = dev_get_by_index_rcu(net, val);
|
||||
if (!dev) {
|
||||
rcu_read_unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
midx = l3mdev_master_ifindex_rcu(dev);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
bound_dev_if = READ_ONCE(sk->sk_bound_dev_if);
|
||||
if (bound_dev_if &&
|
||||
bound_dev_if != val &&
|
||||
(!midx || midx != bound_dev_if))
|
||||
return -EINVAL;
|
||||
}
|
||||
WRITE_ONCE(np->mcast_oif, val);
|
||||
return 0;
|
||||
}
|
||||
if (needs_rtnl)
|
||||
rtnl_lock();
|
||||
@ -860,36 +888,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
||||
break;
|
||||
}
|
||||
|
||||
case IPV6_MULTICAST_IF:
|
||||
if (sk->sk_type == SOCK_STREAM)
|
||||
break;
|
||||
if (optlen < sizeof(int))
|
||||
goto e_inval;
|
||||
|
||||
if (val) {
|
||||
struct net_device *dev;
|
||||
int midx;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
dev = dev_get_by_index_rcu(net, val);
|
||||
if (!dev) {
|
||||
rcu_read_unlock();
|
||||
retv = -ENODEV;
|
||||
break;
|
||||
}
|
||||
midx = l3mdev_master_ifindex_rcu(dev);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (sk->sk_bound_dev_if &&
|
||||
sk->sk_bound_dev_if != val &&
|
||||
(!midx || midx != sk->sk_bound_dev_if))
|
||||
goto e_inval;
|
||||
}
|
||||
np->mcast_oif = val;
|
||||
retv = 0;
|
||||
break;
|
||||
case IPV6_ADD_MEMBERSHIP:
|
||||
case IPV6_DROP_MEMBERSHIP:
|
||||
{
|
||||
@ -1161,10 +1159,12 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
sockopt_release_sock(sk);
|
||||
if (!skb) {
|
||||
if (np->rxopt.bits.rxinfo) {
|
||||
int mcast_oif = READ_ONCE(np->mcast_oif);
|
||||
struct in6_pktinfo src_info;
|
||||
src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
|
||||
|
||||
src_info.ipi6_ifindex = mcast_oif ? :
|
||||
np->sticky_pktinfo.ipi6_ifindex;
|
||||
src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
|
||||
src_info.ipi6_addr = mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr;
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
|
||||
}
|
||||
if (np->rxopt.bits.rxhlim) {
|
||||
@ -1178,11 +1178,13 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
|
||||
}
|
||||
if (np->rxopt.bits.rxoinfo) {
|
||||
int mcast_oif = READ_ONCE(np->mcast_oif);
|
||||
struct in6_pktinfo src_info;
|
||||
src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif :
|
||||
|
||||
src_info.ipi6_ifindex = mcast_oif ? :
|
||||
np->sticky_pktinfo.ipi6_ifindex;
|
||||
src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr :
|
||||
np->sticky_pktinfo.ipi6_addr;
|
||||
src_info.ipi6_addr = mcast_oif ? sk->sk_v6_daddr :
|
||||
np->sticky_pktinfo.ipi6_addr;
|
||||
put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
|
||||
}
|
||||
if (np->rxopt.bits.rxohlim) {
|
||||
@ -1359,7 +1361,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
|
||||
break;
|
||||
|
||||
case IPV6_MULTICAST_IF:
|
||||
val = np->mcast_oif;
|
||||
val = READ_ONCE(np->mcast_oif);
|
||||
break;
|
||||
|
||||
case IPV6_MULTICAST_ALL:
|
||||
|
@ -107,7 +107,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
||||
oif = np->sticky_pktinfo.ipi6_ifindex;
|
||||
|
||||
if (!oif && ipv6_addr_is_multicast(daddr))
|
||||
oif = np->mcast_oif;
|
||||
oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!oif)
|
||||
oif = np->ucast_oif;
|
||||
|
||||
@ -157,7 +157,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
||||
rt = (struct rt6_info *) dst;
|
||||
|
||||
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
|
||||
fl6.flowi6_oif = np->mcast_oif;
|
||||
fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!fl6.flowi6_oif)
|
||||
fl6.flowi6_oif = np->ucast_oif;
|
||||
|
||||
|
@ -876,7 +876,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
||||
final_p = fl6_update_dst(&fl6, opt, &final);
|
||||
|
||||
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
|
||||
fl6.flowi6_oif = np->mcast_oif;
|
||||
fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!fl6.flowi6_oif)
|
||||
fl6.flowi6_oif = np->ucast_oif;
|
||||
security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6));
|
||||
|
@ -1702,7 +1702,7 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
|
||||
!((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
|
||||
if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
|
||||
np->mcast_oif = tcp_v6_iif(opt_skb);
|
||||
WRITE_ONCE(np->mcast_oif, tcp_v6_iif(opt_skb));
|
||||
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
|
||||
WRITE_ONCE(np->mcast_hops,
|
||||
ipv6_hdr(opt_skb)->hop_limit);
|
||||
|
@ -1541,7 +1541,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
||||
connected = false;
|
||||
|
||||
if (!fl6->flowi6_oif && ipv6_addr_is_multicast(&fl6->daddr)) {
|
||||
fl6->flowi6_oif = np->mcast_oif;
|
||||
fl6->flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
connected = false;
|
||||
} else if (!fl6->flowi6_oif)
|
||||
fl6->flowi6_oif = np->ucast_oif;
|
||||
|
@ -599,7 +599,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
||||
final_p = fl6_update_dst(&fl6, opt, &final);
|
||||
|
||||
if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
|
||||
fl6.flowi6_oif = np->mcast_oif;
|
||||
fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
|
||||
else if (!fl6.flowi6_oif)
|
||||
fl6.flowi6_oif = np->ucast_oif;
|
||||
|
||||
|
@ -1365,7 +1365,7 @@ static int set_mcast_if(struct sock *sk, struct net_device *dev)
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
|
||||
/* IPV6_MULTICAST_IF */
|
||||
np->mcast_oif = dev->ifindex;
|
||||
WRITE_ONCE(np->mcast_oif, dev->ifindex);
|
||||
}
|
||||
#endif
|
||||
release_sock(sk);
|
||||
|
@ -165,7 +165,7 @@ int rds_tcp_accept_one(struct socket *sock)
|
||||
struct ipv6_pinfo *inet6;
|
||||
|
||||
inet6 = inet6_sk(new_sock->sk);
|
||||
dev_if = inet6->mcast_oif;
|
||||
dev_if = READ_ONCE(inet6->mcast_oif);
|
||||
} else {
|
||||
dev_if = new_sock->sk->sk_bound_dev_if;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user