Netdev List
 help / color / mirror / Atom feed
* [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
@ 2026-11-05 16:48 Eric Dumazet
  2026-11-05 16:52 ` netdev-bot+sinfo
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Eric Dumazet @ 2026-11-05 16:48 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima,
	Willem de Bruijn
  Cc: Simon Horman, netdev, Eric Dumazet, Kyle Zeng

After listen() + shutdown(), a TCP socket is moved to the closed
section of its reuseport group, while TCP_NEW_SYN_RECV requests
can still reference it as rsk_listener.

If SO_ATTACH_REUSEPORT_[CE]BPF is then used on this socket,
reuseport_attach_prog() calls reuseport_alloc(), which resurrects
it into the listening section of a new group, even though the
socket is still in TCP_CLOSE.

After close(), the socket stays in that group and the pending
requests keep it allocated. When the final ACK arrives,
reuseport_migrate_sock() can select this dead socket and
inet_csk_clone_lock() clones it. With a ULP like espintcp installed
while in TCP_CLOSE, the child inherits icsk_ulp_data and sk_destruct
after tcp_cleanup_ulp() cleared icsk_ulp_ops, leading to a
use-after-free in espintcp_destruct().

As done in commit 8cc3aef0cb19 ("tcp: Do not allow buggy transitions
between ehash and lhash2."), deny this transition instead of adding
checks to the fast path.

Only an unhashed former listener is affected. Applications can still
attach a reuseport prog to a socket that never listened, or to a
listener (including one that called listen() again after shutdown()).

Fixes: 333bb73f620e ("tcp: Keep TCP_CLOSE sockets in the reuseport group.")
Reported-by: Kyle Zeng <kylebot@openai.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
---
 net/core/sock_reuseport.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
index fccbdd0430b05984e82c19b17b6b723b0a3a03a1..41a25be1bfff6aa486f33ed563a093a264c49344 100644
--- a/net/core/sock_reuseport.c
+++ b/net/core/sock_reuseport.c
@@ -6,6 +6,7 @@
  * selecting the socket index from the array of available sockets.
  */
 
+#include <net/inet_connection_sock.h>
 #include <net/ip.h>
 #include <net/sock_reuseport.h>
 #include <linux/bpf.h>
@@ -692,6 +693,14 @@ int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
 		if (!sk->sk_reuseport)
 			return -EINVAL;
 
+		/* Do not put a shutdown()ed TCP listener back into the
+		 * listening section of a reuseport group while it is in
+		 * TCP_CLOSE: requests can still migrate to it.
+		 */
+		if (sk->sk_protocol == IPPROTO_TCP &&
+		    inet_csk(sk)->unhashed_state == TCP_LISTEN)
+			return -EINVAL;
+
 		err = reuseport_alloc(sk, false);
 		if (err)
 			return err;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
  2026-11-05 16:48 [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF Eric Dumazet
@ 2026-11-05 16:52 ` netdev-bot+sinfo
  2026-11-06  6:44 ` Jiayuan Chen
  2026-11-09  4:51 ` netdev-bot+sashiko
  2 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sinfo @ 2026-11-05 16:52 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima,
	Willem de Bruijn, Simon Horman, netdev, Kyle Zeng

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
  2026-11-05 16:48 [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF Eric Dumazet
  2026-11-05 16:52 ` netdev-bot+sinfo
@ 2026-11-06  6:44 ` Jiayuan Chen
  2026-11-06  8:10   ` Eric Dumazet
  2026-11-09  4:51 ` netdev-bot+sashiko
  2 siblings, 1 reply; 6+ messages in thread
From: Jiayuan Chen @ 2026-11-06  6:44 UTC (permalink / raw)
  To: Eric Dumazet, David S . Miller, Jakub Kicinski, Paolo Abeni,
	Kuniyuki Iwashima, Willem de Bruijn
  Cc: Simon Horman, netdev, Kyle Zeng


On 11/6/26 12:48 AM, Eric Dumazet wrote:
> After listen() + shutdown(), a TCP socket is moved to the closed
> section of its reuseport group, while TCP_NEW_SYN_RECV requests
> can still reference it as rsk_listener.
>
> If SO_ATTACH_REUSEPORT_[CE]BPF is then used on this socket,
> reuseport_attach_prog() calls reuseport_alloc(), which resurrects
> it into the listening section of a new group, even though the
> socket is still in TCP_CLOSE.
>
> After close(), the socket stays in that group and the pending
> requests keep it allocated. When the final ACK arrives,
> reuseport_migrate_sock() can select this dead socket and
> inet_csk_clone_lock() clones it. With a ULP like espintcp installed
> while in TCP_CLOSE, the child inherits icsk_ulp_data and sk_destruct
> after tcp_cleanup_ulp() cleared icsk_ulp_ops, leading to a
> use-after-free in espintcp_destruct().
>
> As done in commit 8cc3aef0cb19 ("tcp: Do not allow buggy transitions
> between ehash and lhash2."), deny this transition instead of adding
> checks to the fast path.
>
> Only an unhashed former listener is affected. Applications can still
> attach a reuseport prog to a socket that never listened, or to a
> listener (including one that called listen() again after shutdown()).
>
> Fixes: 333bb73f620e ("tcp: Keep TCP_CLOSE sockets in the reuseport group.")
> Reported-by: Kyle Zeng <kylebot@openai.com>
> Signed-off-by: Eric Dumazet <edumazet@kernel.org>
> ---
>  net/core/sock_reuseport.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
>
> diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
> index fccbdd0430b05984e82c19b17b6b723b0a3a03a1..41a25be1bfff6aa486f33ed563a093a264c49344 100644
> --- a/net/core/sock_reuseport.c
> +++ b/net/core/sock_reuseport.c
> @@ -6,6 +6,7 @@
>   * selecting the socket index from the array of available sockets.
>   */
>  
> +#include <net/inet_connection_sock.h>
>  #include <net/ip.h>
>  #include <net/sock_reuseport.h>
>  #include <linux/bpf.h>
> @@ -692,6 +693,14 @@ int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
>  		if (!sk->sk_reuseport)
>  			return -EINVAL;
>  
> +		/* Do not put a shutdown()ed TCP listener back into the
> +		 * listening section of a reuseport group while it is in
> +		 * TCP_CLOSE: requests can still migrate to it.
> +		 */
> +		if (sk->sk_protocol == IPPROTO_TCP &&
> +		    inet_csk(sk)->unhashed_state == TCP_LISTEN)
> +			return -EINVAL;
> +


I think sk_is_tcp() is better. raw socket still can reach here and inet_csk(sk) is invalid.


>  		err = reuseport_alloc(sk, false);
>  		if (err)
>  			return err;

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
  2026-11-06  6:44 ` Jiayuan Chen
@ 2026-11-06  8:10   ` Eric Dumazet
  2026-11-06  8:40     ` Eric Dumazet
  0 siblings, 1 reply; 6+ messages in thread
From: Eric Dumazet @ 2026-11-06  8:10 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima,
	Willem de Bruijn, Simon Horman, netdev, Kyle Zeng

Le ven. 6 nov. 2026 à 07:44, Jiayuan Chen <jiayuan.chen@linux.dev> a écrit :
>
>
> On 11/6/26 12:48 AM, Eric Dumazet wrote:
> > After listen() + shutdown(), a TCP socket is moved to the closed
> > section of its reuseport group, while TCP_NEW_SYN_RECV requests
> > can still reference it as rsk_listener.
> >
> > If SO_ATTACH_REUSEPORT_[CE]BPF is then used on this socket,
> > reuseport_attach_prog() calls reuseport_alloc(), which resurrects
> > it into the listening section of a new group, even though the
> > socket is still in TCP_CLOSE.
> >
> > After close(), the socket stays in that group and the pending
> > requests keep it allocated. When the final ACK arrives,
> > reuseport_migrate_sock() can select this dead socket and
> > inet_csk_clone_lock() clones it. With a ULP like espintcp installed
> > while in TCP_CLOSE, the child inherits icsk_ulp_data and sk_destruct
> > after tcp_cleanup_ulp() cleared icsk_ulp_ops, leading to a
> > use-after-free in espintcp_destruct().
> >
> > As done in commit 8cc3aef0cb19 ("tcp: Do not allow buggy transitions
> > between ehash and lhash2."), deny this transition instead of adding
> > checks to the fast path.
> >
> > Only an unhashed former listener is affected. Applications can still
> > attach a reuseport prog to a socket that never listened, or to a
> > listener (including one that called listen() again after shutdown()).
> >
> > Fixes: 333bb73f620e ("tcp: Keep TCP_CLOSE sockets in the reuseport group.")
> > Reported-by: Kyle Zeng <kylebot@openai.com>
> > Signed-off-by: Eric Dumazet <edumazet@kernel.org>
> > ---
> >  net/core/sock_reuseport.c | 9 +++++++++
> >  1 file changed, 9 insertions(+)
> >
> > diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
> > index fccbdd0430b05984e82c19b17b6b723b0a3a03a1..41a25be1bfff6aa486f33ed563a093a264c49344 100644
> > --- a/net/core/sock_reuseport.c
> > +++ b/net/core/sock_reuseport.c
> > @@ -6,6 +6,7 @@
> >   * selecting the socket index from the array of available sockets.
> >   */
> >
> > +#include <net/inet_connection_sock.h>
> >  #include <net/ip.h>
> >  #include <net/sock_reuseport.h>
> >  #include <linux/bpf.h>
> > @@ -692,6 +693,14 @@ int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
> >               if (!sk->sk_reuseport)
> >                       return -EINVAL;
> >
> > +             /* Do not put a shutdown()ed TCP listener back into the
> > +              * listening section of a reuseport group while it is in
> > +              * TCP_CLOSE: requests can still migrate to it.
> > +              */
> > +             if (sk->sk_protocol == IPPROTO_TCP &&
> > +                 inet_csk(sk)->unhashed_state == TCP_LISTEN)
> > +                     return -EINVAL;
> > +
>
>
> I think sk_is_tcp() is better. raw socket still can reach here and inet_csk(sk) is invalid.

Yes sashiko reported that. And had the same 'suggestion'.

https://sashiko.dev/#/patchset/20261105164830.1682714-1-edumazet%40kernel.org

SO_ATTACH_REUSEPORT_[CE]BPF does not make any sense for anything other
than TCP or UDP.

I can switch to sk_is_tcp() for net tree, and perform the real fix in net-next.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
  2026-11-06  8:10   ` Eric Dumazet
@ 2026-11-06  8:40     ` Eric Dumazet
  0 siblings, 0 replies; 6+ messages in thread
From: Eric Dumazet @ 2026-11-06  8:40 UTC (permalink / raw)
  To: Jiayuan Chen
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima,
	Willem de Bruijn, Simon Horman, netdev, Kyle Zeng

Le ven. 6 nov. 2026 à 09:10, Eric Dumazet <edumazet@kernel.org> a écrit :
>
> Le ven. 6 nov. 2026 à 07:44, Jiayuan Chen <jiayuan.chen@linux.dev> a écrit :

> > I think sk_is_tcp() is better. raw socket still can reach here and inet_csk(sk) is invalid.
>
> Yes sashiko reported that. And had the same 'suggestion'.
>
> https://sashiko.dev/#/patchset/20261105164830.1682714-1-edumazet%40kernel.org
>
> SO_ATTACH_REUSEPORT_[CE]BPF does not make any sense for anything other
> than TCP or UDP.
>
> I can switch to sk_is_tcp() for net tree, and perform the real fix in net-next.

I forgot:

pw-bot: cr

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF
  2026-11-05 16:48 [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF Eric Dumazet
  2026-11-05 16:52 ` netdev-bot+sinfo
  2026-11-06  6:44 ` Jiayuan Chen
@ 2026-11-09  4:51 ` netdev-bot+sashiko
  2 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sashiko @ 2026-11-09  4:51 UTC (permalink / raw)
  To: edumazet; +Cc: davem, kuba, pabeni, kuniyu, willemb, horms, netdev, kylebot

Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 0 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] The new check in reuseport_attach_prog()
  (net/core/sock_reuseport.c) decides the socket is an…

--

tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF

This patch makes reuseport_attach_prog() return -EINVAL when it is called
on an unhashed TCP socket whose unhashed_state is TCP_LISTEN. Without this,
a socket that was shutdown() after listen() could be put back into the
listening section of a new reuseport group. reuseport_migrate_sock() could
then pick that dead socket, which leads to a use-after-free in
espintcp_destruct().

> diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
> index fccbdd0430b05..41a25be1bfff6 100644
> --- a/net/core/sock_reuseport.c
> +++ b/net/core/sock_reuseport.c

[ ... ]

> @@ -692,6 +693,14 @@ int reuseport_attach_prog(struct sock *sk, struct bpf_prog *prog)
>  		if (!sk->sk_reuseport)
>  			return -EINVAL;
>  
> +		/* Do not put a shutdown()ed TCP listener back into the
> +		 * listening section of a reuseport group while it is in
> +		 * TCP_CLOSE: requests can still migrate to it.
> +		 */
> +		if (sk->sk_protocol == IPPROTO_TCP &&
> +		    inet_csk(sk)->unhashed_state == TCP_LISTEN)
> +			return -EINVAL;

[Severity: High]
Can a raw socket reach this check with sk_protocol == IPPROTO_TCP? For
socket(AF_INET, SOCK_RAW, IPPROTO_TCP), inet_create() does:

    if (SOCK_RAW == sock->type) {
        inet->inet_num = protocol;
    ...
    sk->sk_protocol = protocol;

In that case the object is a struct raw_sock (or raw6_sock for AF_INET6),
not an inet_connection_sock.

As far as I can tell, this path gets there from userspace:

  socket(AF_INET or AF_INET6, SOCK_RAW, IPPROTO_TCP)
    raw_hash_sk() hashes it, since inet_num is 6
  connect(AF_UNSPEC)
    inet_dgram_connect()->__udp_disconnect()->sk->sk_prot->unhash()
    (raw_unhash_sk()), so sk_unhashed() is now true
  setsockopt(SO_REUSEPORT, 1)
    only sk_is_inet() is checked
  setsockopt(SO_ATTACH_REUSEPORT_CBPF), or SO_ATTACH_REUSEPORT_EBPF with a
  BPF_PROG_TYPE_SOCKET_FILTER program
    sk_reuseport_attach_filter()->reuseport_attach_prog()

Neither attach path checks sk_type before it calls reuseport_attach_prog().
Only the SK_REUSEPORT program type does.

In inet_connection_sock, unhashed_state comes after icsk_accept_queue, two
bind bucket pointers, two timer_lists and five u32s. struct raw_sock only
adds icmp_filter, ipmr_table and numa_drop_counters on top of inet_sock.

On !SMP builds, or with lockdep or debug spinlock configs that make
spinlock_t and timer_list larger, does this become a slab-out-of-bounds
read past the raw_sock object? On other configs the read lands in padding
or in the drop counters, which may be uninitialized.

For raw6_sock it reads an unrelated byte inside ipv6_pinfo. If that byte
happens to equal TCP_LISTEN, the attach fails with -EINVAL.

The only privilege needed is CAP_NET_RAW in the netns user namespace, which
an unprivileged user can get with unshare(CLONE_NEWUSER | CLONE_NEWNET).

Would the existing sk_is_tcp() helper in include/net/sock.h work better
here than the bare sk_protocol test?

    return sk_is_inet(sk) &&
           sk->sk_type == SOCK_STREAM &&
           sk->sk_protocol == IPPROTO_TCP;

> +
>  		err = reuseport_alloc(sk, false);
>  		if (err)
>  			return err;

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261105164830.1682714-1-edumazet%40kernel.org

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-09  4:51 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 16:48 [PATCH net] tcp: do not resurrect a former listener with SO_ATTACH_REUSEPORT_[CE]BPF Eric Dumazet
2026-11-05 16:52 ` netdev-bot+sinfo
2026-11-06  6:44 ` Jiayuan Chen
2026-11-06  8:10   ` Eric Dumazet
2026-11-06  8:40     ` Eric Dumazet
2026-11-09  4:51 ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox