udp: Fix wildcard bind conflict check when using hash2
Subsystems (4)
PATCH 1/1 udp: Fix wildcard bind conflict check when using hash2
From: Martin KaFai Lau <[email protected]>
When binding a udp_sock to a local address and port, UDP uses two hashes (udptable->hash and udptable->hash2) for collision detection. The current code switches to "hash2" when hslot->count > 10.
"hash2" is keyed by local address and local port. "hash" is keyed by local port only.
The issue can be shown in the following bind sequence (pseudo code):
bind(fd1, "[fd00::1]:8888") bind(fd2, "[fd00::2]:8888") bind(fd3, "[fd00::3]:8888") bind(fd4, "[fd00::4]:8888") bind(fd5, "[fd00::5]:8888") bind(fd6, "[fd00::6]:8888") bind(fd7, "[fd00::7]:8888") bind(fd8, "[fd00::8]:8888") bind(fd9, "[fd00::9]:8888")
bind(fd10, "[fd00::10]:8888")
/* Correctly return -EADDRINUSE because "hash" is used
* instead of "hash2". udp_lib_lport_inuse() detects the
* conflict.
*/bind(fail_fd, "[::]:8888")
/* After one more socket is bound to "[fd00::11]:8888",
* hslot->count exceeds 10 and "hash2" is used instead. */bind(fd11, "[fd00::11]:8888")bind(fail_fd, "[::]:8888") /* succeeds unexpectedly */
The same issue applies to the IPv4 wildcard address "0.0.0.0" and the IPv4-mapped wildcard address "::ffff:0.0.0.0". For example, if there are existing sockets bound to "192.168.1.[1-11]:8888", then binding "0.0.0.0:8888" or "[::ffff:0.0.0.0]:8888" can also miss the conflict when hslot->count > 10.
TCP inet_csk_get_port() already has the correct check in inet_use_bhash2_on_bind(). Rename it to inet_use_hash2_on_bind() and move it to inet_hashtables.h so udp.c can reuse it in this fix.
--- include/net/inet_hashtables.h | 14 ++++++++++++++ net/ipv4/inet_connection_sock.c | 20 +++----------------- net/ipv4/udp.c | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-)
include/net/inet_hashtables.h +14 -0
@@ -264,6 +264,20 @@ inet_bhashfn_portaddr(const struct inet_hashinfo *hinfo, const struct sock *sk,return &hinfo->bhash2[hash & (hinfo->bhash_size -1 )]; } +static inline bool inet_use_hash2_on_bind (const struct sock *sk) +{ +#if IS_ENABLED (CONFIG_IPV6 ) +if (sk->sk_family ==AF_INET6 ) { +if (ipv6_addr_any (&sk->sk_v6_rcv_saddr)) +return false; + +if (!ipv6_addr_v4mapped (&sk->sk_v6_rcv_saddr)) +return true; + } +#endif +return sk->sk_rcv_saddr !=htonl (INADDR_ANY ); +} +struct inet_bind_hashbucket *inet_bhash2_addr_any_hashbucket (const struct sock *sk,const struct net *net,int port);
net/ipv4/inet_connection_sock.c +3 -17
@@ -154,20 +154,6 @@ bool inet_sk_get_local_port_range(const struct sock *sk, int *low, int *high) }EXPORT_SYMBOL (inet_sk_get_local_port_range); -static bool inet_use_bhash2_on_bind (const struct sock *sk) -{ -#if IS_ENABLED (CONFIG_IPV6 ) -if (sk->sk_family ==AF_INET6 ) { -if (ipv6_addr_any (&sk->sk_v6_rcv_saddr)) -return false; - -if (!ipv6_addr_v4mapped (&sk->sk_v6_rcv_saddr)) -return true; - } -#endif -return sk->sk_rcv_saddr !=htonl (INADDR_ANY ); -} -static bool inet_bind_conflict (const struct sock *sk,struct sock *sk2,kuid_t uid,bool relax,bool reuseport_cb_ok,bool reuseport_ok)
@@ -259,7 +245,7 @@ static int inet_csk_bind_conflict(const struct sock *sk,* checks separately because their spinlocks have to be acquired/released * independently of each other, to prevent possible deadlocks */ -if (inet_use_bhash2_on_bind (sk)) +if (inet_use_hash2_on_bind (sk))return tb2 &&inet_bhash2_conflict (sk, tb2, uid, relax, reuseport_cb_ok, reuseport_ok);
@@ -376,7 +362,7 @@ inet_csk_find_open_port(const struct sock *sk, struct inet_bind_bucket **tb_ret, head = &hinfo->bhash[inet_bhashfn (net, port, hinfo->bhash_size)];spin_lock_bh (&head->lock); -if (inet_use_bhash2_on_bind (sk)) { +if (inet_use_hash2_on_bind (sk)) {if (inet_bhash2_addr_any_conflict (sk, port, l3mdev, relax, false)) goto next_port; }
@@ -562,7 +548,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) check_bind_conflict = false; } -if (check_bind_conflict &&inet_use_bhash2_on_bind (sk)) { +if (check_bind_conflict &&inet_use_hash2_on_bind (sk)) {if (inet_bhash2_addr_any_conflict (sk, port, l3mdev, true, true)) goto fail_unlock; }
net/ipv4/udp.c +1 -1
@@ -287,7 +287,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, }else { hslot =udp_hashslot (udptable, net, snum);spin_lock_bh (&hslot->lock); -if (hslot->count >10 ) { +if (inet_use_hash2_on_bind (sk) && hslot->count >10 ) {int exist;unsigned int slot2 =udp_sk (sk)->udp_portaddr_hash ^ snum;
Signature · 1 line
2.52.0
Quoted 43 lines · Martin KaFai Lau
From: Martin KaFai Lau <[email protected]>
When binding a
udp_sockto a local address and port, UDP uses two hashes (udptable->hashandudptable->hash2) for collision detection. The current code switches to "hash2" whenhslot->count> 10."hash2" is keyed by local address and local port. "hash" is keyed by local port only.
The issue can be shown in the following bind sequence (pseudo code):
bind(fd1, "[fd00::1]:8888") bind(fd2, "[fd00::2]:8888") bind(fd3, "[fd00::3]:8888") bind(fd4, "[fd00::4]:8888") bind(fd5, "[fd00::5]:8888") bind(fd6, "[fd00::6]:8888") bind(fd7, "[fd00::7]:8888") bind(fd8, "[fd00::8]:8888") bind(fd9, "[fd00::9]:8888")
bind(fd10, "[fd00::10]:8888")/* Correctly return
-EADDRINUSEbecause "hash" is used
* instead of "hash2".udp_lib_lport_inuse()detects the
* conflict.
*/bind(fail_fd, "[::]:8888")/* After one more socket is bound to "[fd00::11]:8888",
*hslot->countexceeds 10 and "hash2" is used instead. */bind(fd11, "[fd00::11]:8888")bind(fail_fd, "[::]:8888")/* succeeds unexpectedly */The same issue applies to the IPv4 wildcard address "0.0.0.0" and the IPv4-mapped wildcard address "::ffff:0.0.0.0". For example, if there are existing sockets bound to "192.168.1.[1-11]:8888", then binding "0.0.0.0:8888" or "[::ffff:0.0.0.0]:8888" can also miss the conflict when
hslot->count> 10.
Quoted 8 lines
TCP
inet_csk_get_port()already has the correct check ininet_use_bhash2_on_bind(). Rename it toinet_use_hash2_on_bind()and move it toinet_hashtables.hsoudp.ccan reuse it in this fix.Fixes 30fff9231fad ("udp: bind() optimisation")Reported-by Andrew Onyshchuk <[email protected]>Signed-off-by Martin KaFai Lau <[email protected]>
Confirmed this fixes the buggy scenario, thanks !
---8<---
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
from socket import *
sk = []
for i in range(10):
... s = socket(AF_INET, SOCK_DGRAM)
... s.bind((f'127.0.0.{i + 1}', 8000))
... sk.append(s)...
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
s =
socket(AF_INET, SOCK_DGRAM)s.bind((f'0.0.0.0', 8000))
Traceback (most recent call last):
File "<python-input-6>", line 1, in <module>
s.bind((f'0.0.0.0', 8000))
~~~~~~^^^^^^^^^^^^^^^^^^^^OSError: [Errno 98] Address already in use
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
s.bind((f'127.0.0.11', 8000))s1 =
socket(AF_INET, SOCK_DGRAM)s1.bind((f'0.0.0.0', 8000))
After:
...
Quoted 1 line
Quoted 1 line
Quoted 1 line
s1.bind((f'0.0.0.0', 8000))
Traceback (most recent call last):
File "<python-input-9>", line 1, in <module>
s1.bind((f'0.0.0.0', 8000))
~~~~~~~^^^^^^^^^^^^^^^^^^^^OSError: [Errno 98] Address already in use
---8<---
Jakub Kicinski
maintainerQuoted 1 line · On Fri, 20 Mar 2026 16:07:10 -0700 Kuniyuki Iwashima wrote:
Confirmed this fixes the buggy scenario, thanks !
Should we start a selftest for UDP wildcard bind behavior or extend bind_wildcard.c or it's odd to ask for it now?
Kuniyuki Iwashima
reviewerQuoted 6 lines · Jakub Kicinski
Quoted 1 line · On Fri, 20 Mar 2026 16:07:10 -0700 Kuniyuki Iwashima wrote:
Confirmed this fixes the buggy scenario, thanks !
Should we start a selftest for UDP wildcard bind behavior or extend
bind_wildcard.cor it's odd to ask for it now?
Makes sense.
I simply changed s/SOCK_STREAM/SOCK_DGRAM/ inbind_wildcard.c and all tests passed.
But, this issue requires UDP-specifc setup and test, so it will need its own FIXTURE and test cases (IPv4 & IPv6).
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:
Quoted 8 lines · On Thu, 19 Mar 2026 11:18:17 -0700 you wrote:
From: Martin KaFai Lau <[email protected]>
When binding a
udp_sockto a local address and port, UDP uses two hashes (udptable->hashandudptable->hash2) for collision detection. The current code switches to "hash2" whenhslot->count> 10.[...]
Here is the summary with links:
- [net] udp: Fix wildcard bind conflict check when using hash2
https://git.kernel.org/netdev/net/c/e537dd15d0d4
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html