]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tcp: fix races in tcp_v[46]_err()
authorEric Dumazet <edumazet@google.com>
Tue, 28 May 2024 12:52:53 +0000 (12:52 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 30 May 2024 00:21:36 +0000 (17:21 -0700)
These functions have races when they:

1) Write sk->sk_err
2) call sk_error_report(sk)
3) call tcp_done(sk)

As described in prior patches in this series:

An smp_wmb() is missing.
We should call tcp_done() before sk_error_report(sk)
to have consistent tcp_poll() results on SMP hosts.

Use tcp_done_with_error() where we centralized the
correct sequence.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Neal Cardwell <ncardwell@google.com>
Link: https://lore.kernel.org/r/20240528125253.1966136-5-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp_ipv4.c
net/ipv6/tcp_ipv6.c

index 8f70b8d1d1e5631c5a738c21e6eec3b41f5bd031..041c7eda9abe223205c83eeed11d27ba0d024533 100644 (file)
@@ -611,15 +611,10 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
 
                ip_icmp_error(sk, skb, err, th->dest, info, (u8 *)th);
 
-               if (!sock_owned_by_user(sk)) {
-                       WRITE_ONCE(sk->sk_err, err);
-
-                       sk_error_report(sk);
-
-                       tcp_done(sk);
-               } else {
+               if (!sock_owned_by_user(sk))
+                       tcp_done_with_error(sk, err);
+               else
                        WRITE_ONCE(sk->sk_err_soft, err);
-               }
                goto out;
        }
 
index 750aa681779ca315d37bdd8a7cdfbebb9f4da63a..1ac7502e1bf5b8e342fd945475a5546fc1f890fe 100644 (file)
@@ -490,14 +490,10 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
                ipv6_icmp_error(sk, skb, err, th->dest, ntohl(info), (u8 *)th);
 
-               if (!sock_owned_by_user(sk)) {
-                       WRITE_ONCE(sk->sk_err, err);
-                       sk_error_report(sk);            /* Wake people up to see the error (see connect in sock.c) */
-
-                       tcp_done(sk);
-               } else {
+               if (!sock_owned_by_user(sk))
+                       tcp_done_with_error(sk, err);
+               else
                        WRITE_ONCE(sk->sk_err_soft, err);
-               }
                goto out;
        case TCP_LISTEN:
                break;