From: Juan Agustin Niccolini <juanagusnicco@gmail.com>
To: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com
Cc: horms@kernel.org, ncardwell@google.com, kuniyu@google.com,
josef@toxicpanda.com, jbacik@fb.com, shuah@kernel.org,
linux-kselftest@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org,
Juan Agustin Niccolini <juanagusnicco@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH net v2 1/2] tcp: Fix the SO_REUSEPORT bind cache admitting a conflicting socket
Date: Wed, 11 Nov 2026 12:47:03 -0500 [thread overview]
Message-ID: <20261111174704.24312-2-juanagusnicco@gmail.com> (raw)
In-Reply-To: <20261111174704.24312-1-juanagusnicco@gmail.com>
When the port is in the FASTREUSEPORT_STRICT state, inet_csk_get_port()
skips the bind conflict check if sk_reuseport_match() finds the new
socket's address covered by the socket cached in tb->fast_*.
This is only safe if both sockets match on everything
inet_bind_conflict() reads. The cache misses some of it:
- It does not record sk_bound_dev_if or sk_reuse.
- It records the family and IPV6_V6ONLY but never compares them with
the new socket, so a cached v6only [::] (or 0.0.0.0) socket covers
a dual-stack [::] socket.
As a result, an unprivileged user can bind and listen on a port that
another user is listening on, for example a dual-stack [::] socket on
top of a 0.0.0.0 listener:
victim: bind(0.0.0.0:P), listen()
attacker: SO_REUSEPORT + IPV6_V6ONLY, bind([::]:P) /* fills cache */
attacker: SO_REUSEPORT, bind([::]:P), listen() /* should fail */
Only take the fast path when the new socket matches the cached one in
family, IPV6_V6ONLY, bound device and SO_REUSEADDR. Identical
SO_REUSEPORT listeners, the workload this cache was added for, still
skip the scan (binding 16384 of them: 9.80 ms before, 9.86 ms after,
5874 ms with the full check every time).
Fixes: 637bc8bbe6c0 ("inet: reset tb->fastreuseport when adding a reuseport sk")
Cc: stable@vger.kernel.org
Signed-off-by: Juan Agustin Niccolini <juanagusnicco@gmail.com>
---
include/net/inet_hashtables.h | 2 ++
net/ipv4/inet_connection_sock.c | 15 ++++++++++++---
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 6e2fe186d0dc..44b7e39a1245 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -87,6 +87,8 @@ struct inet_bind_bucket {
__be32 fast_rcv_saddr;
unsigned short fast_sk_family;
bool fast_ipv6_only;
+ unsigned char fast_sk_reuse;
+ int fast_bound_dev_if;
struct hlist_node node;
struct hlist_head bhash2;
struct rcu_head rcu;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index e56beec43877..70b8662b3004 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -420,13 +420,18 @@ static inline int sk_reuseport_match(struct inet_bind_bucket *tb,
return 0;
if (!uid_eq(tb->fastuid, sk_uid(sk)))
return 0;
- /* We only need to check the rcv_saddr if this tb was once marked
+ /* We only need to check the fast_* fields if this tb was once marked
* without fastreuseport and then was reset, as we can only know that
- * the fast_*rcv_saddr doesn't have any conflicts with the socks on the
- * owners list.
+ * the socket they describe doesn't have any conflicts with the socks
+ * on the owners list.
*/
if (tb->fastreuseport == FASTREUSEPORT_ANY)
return 1;
+ if (tb->fast_sk_family != sk->sk_family ||
+ tb->fast_ipv6_only != ipv6_only_sock(sk) ||
+ tb->fast_bound_dev_if != sk->sk_bound_dev_if ||
+ tb->fast_sk_reuse != sk->sk_reuse)
+ return 0;
#if IS_ENABLED(CONFIG_IPV6)
if (tb->fast_sk_family == AF_INET6)
return ipv6_rcv_saddr_equal(&tb->fast_v6_rcv_saddr,
@@ -455,6 +460,8 @@ void inet_csk_update_fastreuse(const struct sock *sk,
tb->fast_rcv_saddr = sk->sk_rcv_saddr;
tb->fast_ipv6_only = ipv6_only_sock(sk);
tb->fast_sk_family = sk->sk_family;
+ tb->fast_sk_reuse = sk->sk_reuse;
+ tb->fast_bound_dev_if = sk->sk_bound_dev_if;
#if IS_ENABLED(CONFIG_IPV6)
tb->fast_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
#endif
@@ -482,6 +489,8 @@ void inet_csk_update_fastreuse(const struct sock *sk,
tb->fast_rcv_saddr = sk->sk_rcv_saddr;
tb->fast_ipv6_only = ipv6_only_sock(sk);
tb->fast_sk_family = sk->sk_family;
+ tb->fast_sk_reuse = sk->sk_reuse;
+ tb->fast_bound_dev_if = sk->sk_bound_dev_if;
#if IS_ENABLED(CONFIG_IPV6)
tb->fast_v6_rcv_saddr = sk->sk_v6_rcv_saddr;
#endif
--
2.43.0
next prev parent reply other threads:[~2026-11-11 17:48 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 17:47 [PATCH net v2 0/2] " Juan Agustin Niccolini
2026-11-11 17:47 ` Juan Agustin Niccolini [this message]
2026-11-11 17:47 ` [PATCH net v2 2/2] selftests: net: Add SO_REUSEPORT bind cache test to bind_wildcard.c Juan Agustin Niccolini
2026-11-11 17:51 ` [PATCH net v2 0/2] tcp: Fix the SO_REUSEPORT bind cache admitting a conflicting socket netdev-bot+sinfo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111174704.24312-2-juanagusnicco@gmail.com \
--to=juanagusnicco@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=jbacik@fb.com \
--cc=josef@toxicpanda.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=shuah@kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®