ipv6: annotate data-races around np->ucast_oif
authorEric Dumazet <edumazet@google.com>
Fri, 8 Dec 2023 10:12:44 +0000 (10:12 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2023 10:59:17 +0000 (10:59 +0000)
np->ucast_oif is read locklessly in some contexts.

Make all accesses to this field lockless, adding appropriate
annotations.

This also makes setsockopt( IPV6_UNICAST_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>
net/ipv6/datagram.c
net/ipv6/icmp.c
net/ipv6/ipv6_sockglue.c
net/ipv6/ping.c
net/ipv6/raw.c
net/ipv6/udp.c
net/l2tp/l2tp_ip6.c

index 1804bd6..fff7849 100644 (file)
@@ -62,7 +62,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6,
                if (ipv6_addr_is_multicast(&fl6->daddr))
                        oif = READ_ONCE(np->mcast_oif);
                else
-                       oif = np->ucast_oif;
+                       oif = READ_ONCE(np->ucast_oif);
        }
 
        fl6->flowi6_oif = oif;
index f84a465..1635da0 100644 (file)
@@ -586,7 +586,7 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
                fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
        else if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->ucast_oif;
+               fl6.flowi6_oif = READ_ONCE(np->ucast_oif);
 
        ipcm6_init_sk(&ipc6, sk);
        ipc6.sockc.mark = mark;
@@ -772,7 +772,7 @@ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
                fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
        else if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->ucast_oif;
+               fl6.flowi6_oif = READ_ONCE(np->ucast_oif);
 
        if (ip6_dst_lookup(net, sk, &dst, &fl6))
                goto out;
index fe7e96e..9e8ebda 100644 (file)
@@ -537,6 +537,31 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                }
                WRITE_ONCE(np->mcast_oif, val);
                return 0;
+       case IPV6_UNICAST_IF:
+       {
+               struct net_device *dev;
+               int ifindex;
+
+               if (optlen != sizeof(int))
+                       return -EINVAL;
+
+               ifindex = (__force int)ntohl((__force __be32)val);
+               if (!ifindex) {
+                       WRITE_ONCE(np->ucast_oif, 0);
+                       return 0;
+               }
+
+               dev = dev_get_by_index(net, ifindex);
+               if (!dev)
+                       return -EADDRNOTAVAIL;
+               dev_put(dev);
+
+               if (READ_ONCE(sk->sk_bound_dev_if))
+                       return -EINVAL;
+
+               WRITE_ONCE(np->ucast_oif, ifindex);
+               return 0;
+       }
        }
        if (needs_rtnl)
                rtnl_lock();
@@ -857,37 +882,6 @@ done:
                break;
        }
 
-
-       case IPV6_UNICAST_IF:
-       {
-               struct net_device *dev = NULL;
-               int ifindex;
-
-               if (optlen != sizeof(int))
-                       goto e_inval;
-
-               ifindex = (__force int)ntohl((__force __be32)val);
-               if (ifindex == 0) {
-                       np->ucast_oif = 0;
-                       retv = 0;
-                       break;
-               }
-
-               dev = dev_get_by_index(net, ifindex);
-               retv = -EADDRNOTAVAIL;
-               if (!dev)
-                       break;
-               dev_put(dev);
-
-               retv = -EINVAL;
-               if (sk->sk_bound_dev_if)
-                       break;
-
-               np->ucast_oif = ifindex;
-               retv = 0;
-               break;
-       }
-
        case IPV6_ADD_MEMBERSHIP:
        case IPV6_DROP_MEMBERSHIP:
        {
@@ -1369,7 +1363,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                break;
 
        case IPV6_UNICAST_IF:
-               val = (__force int)htonl((__u32) np->ucast_oif);
+               val = (__force int)htonl((__u32) READ_ONCE(np->ucast_oif));
                break;
 
        case IPV6_MTU_DISCOVER:
index 465e8d0..ef2059c 100644 (file)
@@ -109,7 +109,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (!oif && ipv6_addr_is_multicast(daddr))
                oif = READ_ONCE(np->mcast_oif);
        else if (!oif)
-               oif = np->ucast_oif;
+               oif = READ_ONCE(np->ucast_oif);
 
        addr_type = ipv6_addr_type(daddr);
        if ((__ipv6_addr_needs_scope_id(addr_type) && !oif) ||
@@ -159,7 +159,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
                fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
        else if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->ucast_oif;
+               fl6.flowi6_oif = READ_ONCE(np->ucast_oif);
 
        pfh.icmph.type = user_icmph.icmp6_type;
        pfh.icmph.code = user_icmph.icmp6_code;
index 59a1e26..03dbb87 100644 (file)
@@ -878,7 +878,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
                fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
        else if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->ucast_oif;
+               fl6.flowi6_oif = READ_ONCE(np->ucast_oif);
        security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6));
 
        if (hdrincl)
index 0b7c755..594e3f2 100644 (file)
@@ -1544,7 +1544,7 @@ do_udp_sendmsg:
                fl6->flowi6_oif = READ_ONCE(np->mcast_oif);
                connected = false;
        } else if (!fl6->flowi6_oif)
-               fl6->flowi6_oif = np->ucast_oif;
+               fl6->flowi6_oif = READ_ONCE(np->ucast_oif);
 
        security_sk_classify_flow(sk, flowi6_to_flowi_common(fl6));
 
index 17301f9..dd31539 100644 (file)
@@ -601,7 +601,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr))
                fl6.flowi6_oif = READ_ONCE(np->mcast_oif);
        else if (!fl6.flowi6_oif)
-               fl6.flowi6_oif = np->ucast_oif;
+               fl6.flowi6_oif = READ_ONCE(np->ucast_oif);
 
        security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6));