Patch "dccp/tcp: Reset saddr on failure after inet6?_hash_connect()." has been added to the 4.14-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    dccp/tcp: Reset saddr on failure after inet6?_hash_connect().

to the 4.14-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     dccp-tcp-reset-saddr-on-failure-after-inet6-_hash_co.patch
and it can be found in the queue-4.14 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 0ed5c5ff2063da87d765434095d9a25cdc1ef8b6
Author: Kuniyuki Iwashima <kuniyu@xxxxxxxxxx>
Date:   Fri Nov 18 17:49:11 2022 -0800

    dccp/tcp: Reset saddr on failure after inet6?_hash_connect().
    
    [ Upstream commit 77934dc6db0d2b111a8f2759e9ad2fb67f5cffa5 ]
    
    When connect() is called on a socket bound to the wildcard address,
    we change the socket's saddr to a local address.  If the socket
    fails to connect() to the destination, we have to reset the saddr.
    
    However, when an error occurs after inet_hash6?_connect() in
    (dccp|tcp)_v[46]_conect(), we forget to reset saddr and leave
    the socket bound to the address.
    
    From the user's point of view, whether saddr is reset or not varies
    with errno.  Let's fix this inconsistent behaviour.
    
    Note that after this patch, the repro [0] will trigger the WARN_ON()
    in inet_csk_get_port() again, but this patch is not buggy and rather
    fixes a bug papering over the bhash2's bug for which we need another
    fix.
    
    For the record, the repro causes -EADDRNOTAVAIL in inet_hash6_connect()
    by this sequence:
    
      s1 = socket()
      s1.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
      s1.bind(('127.0.0.1', 10000))
      s1.sendto(b'hello', MSG_FASTOPEN, (('127.0.0.1', 10000)))
      # or s1.connect(('127.0.0.1', 10000))
    
      s2 = socket()
      s2.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
      s2.bind(('0.0.0.0', 10000))
      s2.connect(('127.0.0.1', 10000))  # -EADDRNOTAVAIL
    
      s2.listen(32)  # WARN_ON(inet_csk(sk)->icsk_bind2_hash != tb2);
    
    [0]: https://syzkaller.appspot.com/bug?extid=015d756bbd1f8b5c8f09
    
    Fixes: 3df80d9320bc ("[DCCP]: Introduce DCCPv6")
    Fixes: 7c657876b63c ("[DCCP]: Initial implementation")
    Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
    Signed-off-by: Kuniyuki Iwashima <kuniyu@xxxxxxxxxx>
    Acked-by: Joanne Koong <joannelkoong@xxxxxxxxx>
    Reviewed-by: Eric Dumazet <edumazet@xxxxxxxxxx>
    Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 7e93087d1366..c021d5dde8f7 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -134,6 +134,8 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 	 * This unhashes the socket and releases the local port, if necessary.
 	 */
 	dccp_set_state(sk, DCCP_CLOSED);
+	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
+		inet_reset_saddr(sk);
 	ip_rt_put(rt);
 	sk->sk_route_caps = 0;
 	inet->inet_dport = 0;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index ae4851fdbe9e..72803e1ea10a 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -957,6 +957,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
 late_failure:
 	dccp_set_state(sk, DCCP_CLOSED);
+	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
+		inet_reset_saddr(sk);
 	__sk_dst_reset(sk);
 failure:
 	inet->inet_dport = 0;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 9d8c64b92011..8bbdd8e36618 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -265,6 +265,8 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 	 * if necessary.
 	 */
 	tcp_set_state(sk, TCP_CLOSE);
+	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
+		inet_reset_saddr(sk);
 	ip_rt_put(rt);
 	sk->sk_route_caps = 0;
 	inet->inet_dport = 0;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 4ef55062d37c..c639431d848c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -310,6 +310,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
 late_failure:
 	tcp_set_state(sk, TCP_CLOSE);
+	if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK))
+		inet_reset_saddr(sk);
 failure:
 	inet->inet_dport = 0;
 	sk->sk_route_caps = 0;



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux