]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
udp: Unhash auto-bound connected sk from 4-tuple hash table when disconnected.
authorKuniyuki Iwashima <kuniyu@google.com>
Fri, 27 Feb 2026 03:55:35 +0000 (03:55 +0000)
committerJakub Kicinski <kuba@kernel.org>
Sat, 28 Feb 2026 15:46:24 +0000 (07:46 -0800)
Let's say we bind() an UDP socket to the wildcard address with a
non-zero port, connect() it to an address, and disconnect it from
the address.

bind() sets SOCK_BINDPORT_LOCK on sk->sk_userlocks (but not
SOCK_BINDADDR_LOCK), and connect() calls udp_lib_hash4() to put
the socket into the 4-tuple hash table.

Then, __udp_disconnect() calls sk->sk_prot->rehash(sk).

It computes a new hash based on the wildcard address and moves
the socket to a new slot in the 4-tuple hash table, leaving a
garbage in the chain that no packet hits.

Let's remove such a socket from 4-tuple hash table when disconnected.

Note that udp_sk(sk)->udp_portaddr_hash needs to be udpated after
udp_hash4_dec(hslot2) in udp_unhash4().

Fixes: 78c91ae2c6de ("ipv4/udp: Add 4-tuple hash for connected socket")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260227035547.3321327-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/udp.c

index 014fdfdd331b98491b02ff4252a493926a60d3b7..b60fad393e1820420378b0a31d7d1e978c231489 100644 (file)
@@ -2287,7 +2287,6 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
                                     udp_sk(sk)->udp_port_hash);
                hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);
                nhslot2 = udp_hashslot2(udptable, newhash);
-               udp_sk(sk)->udp_portaddr_hash = newhash;
 
                if (hslot2 != nhslot2 ||
                    rcu_access_pointer(sk->sk_reuseport_cb)) {
@@ -2321,19 +2320,25 @@ void udp_lib_rehash(struct sock *sk, u16 newhash, u16 newhash4)
                if (udp_hashed4(sk)) {
                        spin_lock_bh(&hslot->lock);
 
-                       udp_rehash4(udptable, sk, newhash4);
-                       if (hslot2 != nhslot2) {
-                               spin_lock(&hslot2->lock);
-                               udp_hash4_dec(hslot2);
-                               spin_unlock(&hslot2->lock);
-
-                               spin_lock(&nhslot2->lock);
-                               udp_hash4_inc(nhslot2);
-                               spin_unlock(&nhslot2->lock);
+                       if (inet_rcv_saddr_any(sk)) {
+                               udp_unhash4(udptable, sk);
+                       } else {
+                               udp_rehash4(udptable, sk, newhash4);
+                               if (hslot2 != nhslot2) {
+                                       spin_lock(&hslot2->lock);
+                                       udp_hash4_dec(hslot2);
+                                       spin_unlock(&hslot2->lock);
+
+                                       spin_lock(&nhslot2->lock);
+                                       udp_hash4_inc(nhslot2);
+                                       spin_unlock(&nhslot2->lock);
+                               }
                        }
 
                        spin_unlock_bh(&hslot->lock);
                }
+
+               udp_sk(sk)->udp_portaddr_hash = newhash;
        }
 }
 EXPORT_IPV6_MOD(udp_lib_rehash);