• Wei Wang's avatar
    tcp: avoid fastopen API to be used on AF_UNSPEC · 96d4f8a1
    Wei Wang authored
    commit ba615f67 upstream.
    
    Fastopen API should be used to perform fastopen operations on the TCP
    socket. It does not make sense to use fastopen API to perform disconnect
    by calling it with AF_UNSPEC. The fastopen data path is also prone to
    race conditions and bugs when using with AF_UNSPEC.
    
    One issue reported and analyzed by Vegard Nossum is as follows:
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Thread A:                            Thread B:
    ------------------------------------------------------------------------
    sendto()
     - tcp_sendmsg()
         - sk_stream_memory_free() = 0
             - goto wait_for_sndbuf
    	     - sk_stream_wait_memory()
    	        - sk_wait_event() // sleep
              |                          sendto(flags=MSG_FASTOPEN, dest_addr=AF_UNSPEC)
    	  |                           - tcp_sendmsg()
    	  |                              - tcp_sendmsg_fastopen()
    	  |                                 - __inet_stream_connect()
    	  |                                    - tcp_disconnect() //because of AF_UNSPEC
    	  |                                       - tcp_transmit_skb()// send RST
    	  |                                    - return 0; // no reconnect!
    	  |                           - sk_stream_wait_connect()
    	  |                                 - sock_error()
    	  |                                    - xchg(&sk->sk_err, 0)
    	  |                                    - return -ECONNRESET
    	- ... // wake up, see sk->sk_err == 0
        - skb_entail() on TCP_CLOSE socket
    
    If the connection is reopened then we will send a brand new SYN packet
    after thread A has already queued a buffer. At this point I think the
    socket internal state (sequence numbers etc.) becomes messed up.
    
    When the new connection is closed, the FIN-ACK is rejected because the
    sequence number is outside the window. The other side tries to
    retransmit,
    but __tcp_retransmit_skb() calls tcp_trim_head() on an empty skb which
    corrupts the skb data length and hits a BUG() in copy_and_csum_bits().
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    
    Hence, this patch adds a check for AF_UNSPEC in the fastopen data path
    and return EOPNOTSUPP to user if such case happens.
    
    Fixes: cf60af03 ("tcp: Fast Open client - sendmsg(MSG_FASTOPEN)")
    Reported-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
    Signed-off-by: default avatarWei Wang <weiwan@google.com>
    Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    [bwh: Backported to 3.16: adjust context]
    Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
    96d4f8a1
tcp.c 83.5 KB