inet: move inet->recverr to inet->inet_flags
authorEric Dumazet <edumazet@google.com>
Wed, 16 Aug 2023 08:15:35 +0000 (08:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 Aug 2023 10:09:17 +0000 (11:09 +0100)
IP_RECVERR socket option can now be set/get without locking the socket.

This patch potentially avoid data-races around inet->recverr.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/inet_sock.h
net/dccp/ipv4.c
net/ipv4/inet_diag.c
net/ipv4/ip_sockglue.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/tcp_ipv4.c
net/ipv4/udp.c
net/sctp/input.c

index e3b35b0..552188a 100644 (file)
@@ -230,8 +230,7 @@ struct inet_sock {
        __u8                    min_ttl;
        __u8                    mc_ttl;
        __u8                    pmtudisc;
-       __u8                    recverr:1,
-                               is_icsk:1,
+       __u8                    is_icsk:1,
                                freebind:1,
                                hdrincl:1,
                                mc_loop:1,
@@ -270,6 +269,8 @@ enum {
        INET_FLAGS_ORIGDSTADDR  = 6,
        INET_FLAGS_CHECKSUM     = 7,
        INET_FLAGS_RECVFRAGSIZE = 8,
+
+       INET_FLAGS_RECVERR      = 9,
 };
 
 /* cmsg flags for inet */
index 8e919cf..8dd6837 100644 (file)
@@ -247,7 +247,6 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
        const u8 offset = iph->ihl << 2;
        const struct dccp_hdr *dh;
        struct dccp_sock *dp;
-       struct inet_sock *inet;
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
        struct sock *sk;
@@ -361,8 +360,7 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
         *                                                      --ANK (980905)
         */
 
-       inet = inet_sk(sk);
-       if (!sock_owned_by_user(sk) && inet->recverr) {
+       if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) {
                sk->sk_err = err;
                sk_error_report(sk);
        } else { /* Only an error on timeout */
index f742692..25d5f76 100644 (file)
@@ -182,7 +182,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
        r->idiag_inode = sock_i_ino(sk);
 
        memset(&inet_sockopt, 0, sizeof(inet_sockopt));
-       inet_sockopt.recverr    = inet->recverr;
+       inet_sockopt.recverr    = inet_test_bit(RECVERR, sk);
        inet_sockopt.is_icsk    = inet->is_icsk;
        inet_sockopt.freebind   = inet->freebind;
        inet_sockopt.hdrincl    = inet->hdrincl;
index 69b8751..8283d86 100644 (file)
@@ -446,12 +446,11 @@ EXPORT_SYMBOL_GPL(ip_icmp_error);
 
 void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
 {
-       struct inet_sock *inet = inet_sk(sk);
        struct sock_exterr_skb *serr;
        struct iphdr *iph;
        struct sk_buff *skb;
 
-       if (!inet->recverr)
+       if (!inet_test_bit(RECVERR, sk))
                return;
 
        skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
@@ -617,9 +616,7 @@ EXPORT_SYMBOL(ip_sock_set_freebind);
 
 void ip_sock_set_recverr(struct sock *sk)
 {
-       lock_sock(sk);
-       inet_sk(sk)->recverr = true;
-       release_sock(sk);
+       inet_set_bit(RECVERR, sk);
 }
 EXPORT_SYMBOL(ip_sock_set_recverr);
 
@@ -978,6 +975,11 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
                        return -EINVAL;
                inet_assign_bit(RECVFRAGSIZE, sk, val);
                return 0;
+       case IP_RECVERR:
+               inet_assign_bit(RECVERR, sk, val);
+               if (!val)
+                       skb_queue_purge(&sk->sk_error_queue);
+               return 0;
        }
 
        err = 0;
@@ -1064,11 +1066,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
                        goto e_inval;
                inet->pmtudisc = val;
                break;
-       case IP_RECVERR:
-               inet->recverr = !!val;
-               if (!val)
-                       skb_queue_purge(&sk->sk_error_queue);
-               break;
        case IP_RECVERR_RFC4884:
                if (val < 0 || val > 1)
                        goto e_inval;
@@ -1575,6 +1572,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
        case IP_RECVFRAGSIZE:
                val = inet_test_bit(RECVFRAGSIZE, sk);
                goto copyval;
+       case IP_RECVERR:
+               val = inet_test_bit(RECVERR, sk);
+               goto copyval;
        }
 
        if (needs_rtnl)
@@ -1649,9 +1649,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
                }
                break;
        }
-       case IP_RECVERR:
-               val = inet->recverr;
-               break;
        case IP_RECVERR_RFC4884:
                val = inet->recverr_rfc4884;
                break;
index 7e8702c..75e0aee 100644 (file)
@@ -580,7 +580,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info)
         *      RFC1122: OK.  Passes ICMP errors back to application, as per
         *      4.1.3.3.
         */
-       if ((family == AF_INET && !inet_sock->recverr) ||
+       if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) ||
            (family == AF_INET6 && !inet6_sk(sk)->recverr)) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
index e6e813f..f4c27dc 100644 (file)
@@ -203,8 +203,9 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
        struct inet_sock *inet = inet_sk(sk);
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
-       int err = 0;
        int harderr = 0;
+       bool recverr;
+       int err = 0;
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
                ipv4_sk_update_pmtu(skb, sk, info);
@@ -218,7 +219,8 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
           2. Socket is connected (otherwise the error indication
              is useless without ip_recverr and error is hard.
         */
-       if (!inet->recverr && sk->sk_state != TCP_ESTABLISHED)
+       recverr = inet_test_bit(RECVERR, sk);
+       if (!recverr && sk->sk_state != TCP_ESTABLISHED)
                return;
 
        switch (type) {
@@ -245,7 +247,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
                }
        }
 
-       if (inet->recverr) {
+       if (recverr) {
                const struct iphdr *iph = (const struct iphdr *)skb->data;
                u8 *payload = skb->data + (iph->ihl << 2);
 
@@ -254,7 +256,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
                ip_icmp_error(sk, skb, err, 0, info, payload);
        }
 
-       if (inet->recverr || harderr) {
+       if (recverr || harderr) {
                sk->sk_err = err;
                sk_error_report(sk);
        }
@@ -413,7 +415,7 @@ error_free:
        kfree_skb(skb);
 error:
        IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
-       if (err == -ENOBUFS && !inet->recverr)
+       if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk))
                err = 0;
        return err;
 }
@@ -645,7 +647,7 @@ back_from_confirm:
                        ip_flush_pending_frames(sk);
                else if (!(msg->msg_flags & MSG_MORE)) {
                        err = ip_push_pending_frames(sk, &fl4);
-                       if (err == -ENOBUFS && !inet->recverr)
+                       if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk))
                                err = 0;
                }
                release_sock(sk);
index 5b18a04..2a662d5 100644 (file)
@@ -477,7 +477,6 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
        const struct iphdr *iph = (const struct iphdr *)skb->data;
        struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
        struct tcp_sock *tp;
-       struct inet_sock *inet;
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
        struct sock *sk;
@@ -625,8 +624,8 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
         *                                                      --ANK (980905)
         */
 
-       inet = inet_sk(sk);
-       if (!sock_owned_by_user(sk) && inet->recverr) {
+       if (!sock_owned_by_user(sk) &&
+           inet_test_bit(RECVERR, sk)) {
                WRITE_ONCE(sk->sk_err, err);
                sk_error_report(sk);
        } else  { /* Only an error on timeout */
index 4b79113..0794a2c 100644 (file)
@@ -779,7 +779,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
                                                  (u8 *)(uh+1));
                goto out;
        }
-       if (!inet->recverr) {
+       if (!inet_test_bit(RECVERR, sk)) {
                if (!harderr || sk->sk_state != TCP_ESTABLISHED)
                        goto out;
        } else
@@ -962,7 +962,8 @@ csum_partial:
 send:
        err = ip_send_skb(sock_net(sk), skb);
        if (err) {
-               if (err == -ENOBUFS && !inet->recverr) {
+               if (err == -ENOBUFS &&
+                   !inet_test_bit(RECVERR, sk)) {
                        UDP_INC_STATS(sock_net(sk),
                                      UDP_MIB_SNDBUFERRORS, is_udplite);
                        err = 0;
index 2613c4d..17fcaa9 100644 (file)
@@ -581,7 +581,7 @@ static void sctp_v4_err_handle(struct sctp_transport *t, struct sk_buff *skb,
        default:
                return;
        }
-       if (!sock_owned_by_user(sk) && inet_sk(sk)->recverr) {
+       if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) {
                sk->sk_err = err;
                sk_error_report(sk);
        } else {  /* Only an error on timeout */