inet_unhash() sets inet_csk(sk)->unhashed_state only when the
socket is hashed because tcp_set_state(sk, TCP_CLOSE) could be
called multiple times, e.g. tcp_abort() calls it directly and
tcp_done_with_error().
However, inet_twsk_hashdance_schedule() also unhashes a socket
when replacing it with twsk, allowing the socket to bypass
checks for inet_csk(sk)->unhashed_state.
Let's update inet_csk(sk)->unhashed_state there as well.
Fixes: 8cc3aef0cb19 ("tcp: Do not allow buggy transitions between ehash and lhash2.")
Reported-by: Daniel Zahka <daniel.zahka@gmail.com>
Closes: https://lore.kernel.org/netdev/DLHLRA8GVI5B.2Q1IRQG5BVJNZ@gmail.com/ (local)
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
net/ipv4/inet_timewait_sock.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index d4c781a0667f..a1d86026aefb 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -105,10 +105,12 @@ void inet_twsk_hashdance_schedule(struct inet_timewait_sock *tw,
struct inet_hashinfo *hashinfo,
int timeo)
{
- const struct inet_sock *inet = inet_sk(sk);
- const struct inet_connection_sock *icsk = inet_csk(sk);
spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
+ struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_bind_hashbucket *bhead, *bhead2;
+ const struct inet_sock *inet = inet_sk(sk);
+
+ icsk->unhashed_state = sk->sk_state;
/* Put TW into bind hash. Original socket stays there too.
* Note, that any socket with inet->num != 0 MUST be bound in--
2.55.0.1082.g2b9226bbc0-goog