diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 76df69aa0115..0871886a34a2 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5127,6 +5127,8 @@ void tcp_done(struct sock *sk) int tcp_abort(struct sock *sk, int err) { int state = inet_sk_state_load(sk); + bool listener = false; + int ret = 0; if (state == TCP_NEW_SYN_RECV) { struct request_sock *req = inet_reqsk(sk); @@ -5161,21 +5163,31 @@ int tcp_abort(struct sock *sk, int err) if (sk->sk_state == TCP_LISTEN) { tcp_set_state(sk, TCP_CLOSE); inet_csk_listen_stop(sk); + listener = true; } /* Don't race with BH socket closes such as inet_csk_listen_stop. */ local_bh_disable(); bh_lock_sock(sk); + /* An unaccepted child might have been closed by + * inet_csk_listen_stop() while we were not holding its BH lock. + */ + if (!listener && sk->sk_state == TCP_CLOSE) { + ret = -ENOENT; + goto out; + } + if (tcp_need_reset(sk->sk_state)) tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE); tcp_done_with_error(sk, err); +out: bh_unlock_sock(sk); local_bh_enable(); if (!has_current_bpf_ctx()) release_sock(sk); - return 0; + return ret; } EXPORT_SYMBOL_GPL(tcp_abort);