• Eric Dumazet's avatar
    tcp: fix race in tcp_write_err() · 853c3bd7
    Eric Dumazet authored
    I noticed flakes in a packetdrill test, expecting an epoll_wait()
    to return EPOLLERR | EPOLLHUP on a failed connect() attempt,
    after multiple SYN retransmits. It sometimes return EPOLLERR only.
    
    The issue is that tcp_write_err():
     1) writes an error in sk->sk_err,
     2) calls sk_error_report(),
     3) then calls tcp_done().
    
    tcp_done() is writing SHUTDOWN_MASK into sk->sk_shutdown,
    among other things.
    
    Problem is that the awaken user thread (from 2) sk_error_report())
    might call tcp_poll() before tcp_done() has written sk->sk_shutdown.
    
    tcp_poll() only sees a non zero sk->sk_err and returns EPOLLERR.
    
    This patch fixes the issue by making sure to call sk_error_report()
    after tcp_done().
    
    tcp_write_err() also lacks an smp_wmb().
    
    We can reuse tcp_done_with_error() to factor out the details,
    as Neal suggested.
    
    Fixes: 1da177e4 ("Linux-2.6.12-rc2")
    Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
    Acked-by: default avatarNeal Cardwell <ncardwell@google.com>
    Link: https://lore.kernel.org/r/20240528125253.1966136-3-edumazet@google.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
    853c3bd7
tcp_timer.c 25.1 KB