* [PATCH net] udp: Fix wildcard bind conflict check when using hash2
@ 2026-03-19 18:18 Martin KaFai Lau
2026-03-20 23:07 ` Kuniyuki Iwashima
2026-03-24 2:00 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 5+ messages in thread
From: Martin KaFai Lau @ 2026-03-19 18:18 UTC (permalink / raw)
To: netdev
Cc: 'David Miller ', 'Jakub Kicinski ',
'Eric Dumazet ', 'Paolo Abeni ',
'Kuniyuki Iwashima ', Andrew Onyshchuk
From: Martin KaFai Lau <martin.lau@kernel.org>
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.
Fixes: 30fff9231fad ("udp: bind() optimisation")
Reported-by: Andrew Onyshchuk <oandrew@meta.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
---
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(-)
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 5a979dcab538..6d936e9f2fd3 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -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);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 5dfac6ce1110..e961936b6be7 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -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;
}
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index b60fad393e18..cb99a3c27053 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -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;
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net] udp: Fix wildcard bind conflict check when using hash2
2026-03-19 18:18 [PATCH net] udp: Fix wildcard bind conflict check when using hash2 Martin KaFai Lau
@ 2026-03-20 23:07 ` Kuniyuki Iwashima
2026-03-21 0:59 ` Jakub Kicinski
2026-03-24 2:00 ` patchwork-bot+netdevbpf
1 sibling, 1 reply; 5+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-20 23:07 UTC (permalink / raw)
To: Martin KaFai Lau
Cc: netdev, David Miller, Jakub Kicinski, Eric Dumazet, Paolo Abeni,
Andrew Onyshchuk
On Thu, Mar 19, 2026 at 11:18 AM Martin KaFai Lau <martin.lau@linux.dev> wrote:
>
> From: Martin KaFai Lau <martin.lau@kernel.org>
>
> 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.
>
> Fixes: 30fff9231fad ("udp: bind() optimisation")
> Reported-by: Andrew Onyshchuk <oandrew@meta.com>
> Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Confirmed this fixes the buggy scenario, thanks !
---8<---
>>> 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)
...
>>>
>>> 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
>>> s.bind((f'127.0.0.11', 8000))
>>>
>>> s1 = socket(AF_INET, SOCK_DGRAM)
>>> s1.bind((f'0.0.0.0', 8000))
>>>
After:
...
>>> 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<---
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net] udp: Fix wildcard bind conflict check when using hash2
2026-03-20 23:07 ` Kuniyuki Iwashima
@ 2026-03-21 0:59 ` Jakub Kicinski
2026-03-21 13:44 ` Kuniyuki Iwashima
0 siblings, 1 reply; 5+ messages in thread
From: Jakub Kicinski @ 2026-03-21 0:59 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: Martin KaFai Lau, netdev, David Miller, Eric Dumazet, Paolo Abeni,
Andrew Onyshchuk
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?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net] udp: Fix wildcard bind conflict check when using hash2
2026-03-21 0:59 ` Jakub Kicinski
@ 2026-03-21 13:44 ` Kuniyuki Iwashima
0 siblings, 0 replies; 5+ messages in thread
From: Kuniyuki Iwashima @ 2026-03-21 13:44 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Martin KaFai Lau, netdev, David Miller, Eric Dumazet, Paolo Abeni,
Andrew Onyshchuk
On Fri, Mar 20, 2026 at 5:59 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> 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?
Makes sense.
I simply changed s/SOCK_STREAM/SOCK_DGRAM/ in
bind_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).
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH net] udp: Fix wildcard bind conflict check when using hash2
2026-03-19 18:18 [PATCH net] udp: Fix wildcard bind conflict check when using hash2 Martin KaFai Lau
2026-03-20 23:07 ` Kuniyuki Iwashima
@ 2026-03-24 2:00 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-03-24 2:00 UTC (permalink / raw)
To: Martin KaFai Lau; +Cc: netdev, davem, kuba, edumazet, pabeni, kuniyu, oandrew
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Thu, 19 Mar 2026 11:18:17 -0700 you wrote:
> From: Martin KaFai Lau <martin.lau@kernel.org>
>
> 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.
>
> [...]
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!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-03-24 2:00 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-19 18:18 [PATCH net] udp: Fix wildcard bind conflict check when using hash2 Martin KaFai Lau
2026-03-20 23:07 ` Kuniyuki Iwashima
2026-03-21 0:59 ` Jakub Kicinski
2026-03-21 13:44 ` Kuniyuki Iwashima
2026-03-24 2:00 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox