* [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen()
@ 2026-09-02 17:52 Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 1/2] bpf: Unconditionally take socket references in lookup helpers Michal Luczaj
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Michal Luczaj @ 2026-09-02 17:52 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
John Fastabend, Stanislav Fomichev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter
Cc: Michal Luczaj, bpf, netdev, linux-kernel, netfilter-devel,
coreteam, Sashiko
This is a follow-up to Sashiko's report[1].
Several BPF socket helpers acquire a socket reference only when
sk_is_refcounted() == true, and release it, independently, by
re-evaluating sk_is_refcounted() again at the time the release runs. TCP
connect(AF_UNSPEC)+listen() sets SOCK_RCU_FREE on an established socket.
If that happens while a reference is outstanding, the release side sees
sk_is_refcounted() == false and skips the put; the socket is leaked.
unreferenced object 0xffff88811617ce00 (size 3200):
comm "softirq", pid 0, jiffies 4294848512
hex dump (first 32 bytes):
7f 00 00 01 7f 00 00 01 4d 43 02 f6 00 00 00 00 ........MC......
02 00 07 41 00 00 00 00 00 00 00 00 00 00 00 00 ...A............
backtrace (crc fb5bd4c8):
kmem_cache_alloc_noprof+0x53e/0x640
sk_prot_alloc+0x69/0x240
sk_clone+0x79/0x1230
inet_csk_clone_lock+0x30/0x760
tcp_create_openreq_child+0x34/0x2750
tcp_v4_syn_recv_sock+0x12e/0x1080
tcp_check_req+0x447/0x2310
tcp_v4_rcv+0x1026/0x3c90
ip_protocol_deliver_rcu+0x93/0x340
ip_local_deliver_finish+0x356/0x5c0
ip_local_deliver+0x184/0x4a0
ip_rcv+0x4f4/0x5b0
__netif_receive_skb_one_core+0x153/0x1b0
process_backlog+0x28d/0x1190
__napi_poll+0xab/0x520
net_rx_action+0x3f0/0xca0
[1]: https://lore.kernel.org/bpf/20260701235552.2B0AA1F00A3F@smtp.kernel.org/
Signed-off-by: Michal Luczaj <mhal@rbox.co>
---
Changes in v3:
- Add a fix for TC bpf_sk_assign() (patch #2)
- Clean up commit messages
- Non-fixes went to bpf-next: https://lore.kernel.org/bpf/20260813-sockmap-lookup-get-ref-v1-0-31f5d55f44ac@rbox.co/
- Link to v2: https://patch.msgid.link/20260803-sockmap-lookup-tcp-leak-v2-0-306e025bfe66@rbox.co
Changes in v2:
- Defer "Use sock_hold() instead of refcount_inc_not_zero() in lookup" to
bpf-next [John]
- Fix comment style [Sashiko]
- Link to v1: https://patch.msgid.link/20260723-sockmap-lookup-tcp-leak-v1-0-1bbdc58ce514@rbox.co
To: Alexei Starovoitov <ast@kernel.org>
To: Daniel Borkmann <daniel@iogearbox.net>
To: Andrii Nakryiko <andrii@kernel.org>
To: Eduard Zingerman <eddyz87@gmail.com>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: Martin KaFai Lau <martin.lau@linux.dev>
To: Song Liu <song@kernel.org>
To: Yonghong Song <yonghong.song@linux.dev>
To: Jiri Olsa <jolsa@kernel.org>
To: Emil Tsalapatis <emil@etsalapatis.com>
To: John Fastabend <john.fastabend@gmail.com>
To: Stanislav Fomichev <sdf@fomichev.me>
To: "David S. Miller" <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Simon Horman <horms@kernel.org>
To: Kuniyuki Iwashima <kuniyu@google.com>
To: Willem de Bruijn <willemb@google.com>
To: Jakub Sitnicki <jakub@cloudflare.com>
To: Jiayuan Chen <jiayuan.chen@linux.dev>
To: Joe Stringer <joe@wand.net.nz>
To: Ihor Solodrai <ihor.solodrai@linux.dev>
To: Pablo Neira Ayuso <pablo@netfilter.org>
To: Florian Westphal <fw@strlen.de>
To: Phil Sutter <phil@nwl.cc>
Cc: bpf@vger.kernel.org
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: netfilter-devel@vger.kernel.org
Cc: coreteam@netfilter.org
---
Michal Luczaj (2):
bpf: Unconditionally take socket references in lookup helpers
bpf: Fix reference leak in bpf_sk_assign()
include/net/inet6_hashtables.h | 9 ++++----
include/net/inet_hashtables.h | 9 ++++----
include/net/request_sock.h | 2 +-
include/net/sock.h | 11 ++++++++++
net/core/filter.c | 49 +++++++++++++++++++++++++++++-------------
net/core/sock_map.c | 8 ++-----
net/netfilter/nf_queue.c | 18 ++++++++--------
7 files changed, 67 insertions(+), 39 deletions(-)
---
base-commit: ce6b9e5dd873de532cd924e2abc928220cdc2738
change-id: 20260628-sockmap-lookup-tcp-leak-bdaba3e083c5
Best regards,
--
Michal Luczaj <mhal@rbox.co>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH bpf v3 1/2] bpf: Unconditionally take socket references in lookup helpers
2026-09-02 17:52 [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Michal Luczaj
@ 2026-09-02 17:52 ` Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign() Michal Luczaj
2026-09-02 18:58 ` [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Kuniyuki Iwashima
2 siblings, 0 replies; 8+ messages in thread
From: Michal Luczaj @ 2026-09-02 17:52 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
John Fastabend, Stanislav Fomichev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter
Cc: Michal Luczaj, bpf, netdev, linux-kernel, netfilter-devel,
coreteam, Sashiko
Lookup helpers gate the reference acquisition on sk_is_refcounted(), and
the release side re-evaluates the same check. An established socket can be
refcounted at acquire time and later gain SOCK_RCU_FREE via
connect(AF_UNSPEC) + listen() before release. The release re-check then
sees sk_is_refcounted() == false and skips the put, leaking the reference.
Make acquire and release unconditional and symmetric: always take a
reference, always put it. Adapt sk_select_reuseport(). Drop the
misplaced/outdated comment above __bpf_skc_lookup().
Fixes: 6acc9b432e67 ("bpf: Add helper to retrieve socket in BPF")
Fixes: 64d85290d79c ("bpf: Allow bpf_map_lookup_elem for SOCKMAP and SOCKHASH")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/bpf/20260701235552.2B0AA1F00A3F@smtp.kernel.org/
Signed-off-by: Michal Luczaj <mhal@rbox.co>
Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
---
net/core/filter.c | 30 ++++++++++++++++++------------
net/core/sock_map.c | 8 ++------
2 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/net/core/filter.c b/net/core/filter.c
index 61940e753552..66d83a198ea2 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -7205,12 +7205,17 @@ static struct sock *sk_lookup(struct net *net, struct bpf_sock_tuple *tuple,
WARN_ONCE(1, "Found non-RCU, unreferenced socket!");
sk = NULL;
}
+
+ /*
+ * Always take a reference, even if the lookup skipped one;
+ * bpf_sk_release() always puts one.
+ */
+ if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt))
+ sk = NULL;
+
return sk;
}
-/* bpf_skc_lookup performs the core lookup for different types of sockets,
- * taking a reference on the socket if it doesn't have the flag SOCK_RCU_FREE.
- */
static struct sock *
__bpf_skc_lookup(struct sk_buff *skb, struct bpf_sock_tuple *tuple, u32 len,
struct net *caller_net, u32 ifindex, u8 proto, u64 netns_id,
@@ -7263,11 +7268,16 @@ bpf_sk_lookup_full_sk(struct sock *sk)
*/
if (sk2 != sk) {
sock_gen_put(sk);
- /* Ensure there is no need to bump sk2 refcnt. */
if (unlikely(sk2 && !sock_flag(sk2, SOCK_RCU_FREE))) {
WARN_ONCE(1, "Found non-RCU, unreferenced socket!");
return NULL;
}
+ /*
+ * sk2 is not refcounted, but take a reference anyway;
+ * bpf_sk_release() puts.
+ */
+ if (sk2 && !refcount_inc_not_zero(&sk2->sk_refcnt))
+ sk2 = NULL;
sk = sk2;
}
@@ -7448,7 +7458,7 @@ static const struct bpf_func_proto bpf_tc_sk_lookup_udp_proto = {
BPF_CALL_1(bpf_sk_release, struct sock *, sk)
{
- if (sk && sk_is_refcounted(sk))
+ if (sk)
sock_gen_put(sk);
return 0;
}
@@ -11736,11 +11746,13 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
bool is_sockarray = map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY;
struct sock_reuseport *reuse;
struct sock *selected_sk;
- int err;
+ int err = 0;
selected_sk = map->ops->map_lookup_elem(map, key);
if (!selected_sk)
return -ENOENT;
+ if (!is_sockarray)
+ sock_put(selected_sk);
reuse = rcu_dereference(selected_sk->sk_reuseport_cb);
if (!reuse) {
@@ -11770,13 +11782,7 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern,
}
reuse_kern->selected_sk = selected_sk;
-
- return 0;
error:
- /* Lookup in sock_map can return TCP ESTABLISHED sockets. */
- if (sk_is_refcounted(selected_sk))
- sock_put(selected_sk);
-
return err;
}
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index ca49bc7f8687..ae18dc4d60f9 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -390,9 +390,7 @@ static void *sock_map_lookup(struct bpf_map *map, void *key)
struct sock *sk;
sk = __sock_map_lookup_elem(map, *(u32 *)key);
- if (!sk)
- return NULL;
- if (sk_is_refcounted(sk))
+ if (sk)
sock_hold(sk);
return sk;
}
@@ -1216,9 +1214,7 @@ static void *sock_hash_lookup(struct bpf_map *map, void *key)
struct sock *sk;
sk = __sock_hash_lookup_elem(map, key);
- if (!sk)
- return NULL;
- if (sk_is_refcounted(sk))
+ if (sk)
sock_hold(sk);
return sk;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign()
2026-09-02 17:52 [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 1/2] bpf: Unconditionally take socket references in lookup helpers Michal Luczaj
@ 2026-09-02 17:52 ` Michal Luczaj
2026-09-02 18:38 ` sashiko-bot
2026-09-02 18:58 ` [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Kuniyuki Iwashima
2 siblings, 1 reply; 8+ messages in thread
From: Michal Luczaj @ 2026-09-02 17:52 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
John Fastabend, Stanislav Fomichev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter
Cc: Michal Luczaj, bpf, netdev, linux-kernel, netfilter-devel,
coreteam
sk_is_refcounted() is mutable; it depends on sk_state and SOCK_RCU_FREE.
TC bpf_sk_assign() uses it at assign time to take a reference and lets
sock_pfree() re-evaluate it at free time. If the socket becomes
non-refcounted in between, e.g. connect(AF_UNSPEC) + listen() sets
SOCK_RCU_FREE, the reference is leaked.
Freeze the decision at assign time. If the socket is refcounted (i.e. can
become non-refcounted), take a reference and set sock_pfree_refcounted()
as the destructor; otherwise stick with sock_pfree(), which effectively
becomes a no-op destructor. Next, extend skb_sk_is_prefetched() to handle
both dtors and, for the sake of CONFIG_INET=n, add a specialized
skb_sk_is_prefetched_noref(). Adapt prefetched-skb treatment in
__nf_queue() and skb_steal_sock(). The latter changes its contract with
callers: inet{,6}_steal_sock() must now expect a refcounted TCP_LISTEN
socket.
Fixes: 7ae215d23c12 ("bpf: Don't refcount LISTEN sockets in sk_assign()")
Signed-off-by: Michal Luczaj <mhal@rbox.co>
---
include/net/inet6_hashtables.h | 9 +++++----
include/net/inet_hashtables.h | 9 +++++----
include/net/request_sock.h | 2 +-
include/net/sock.h | 11 +++++++++++
net/core/filter.c | 19 ++++++++++++++++---
net/netfilter/nf_queue.c | 18 +++++++++---------
6 files changed, 47 insertions(+), 21 deletions(-)
diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h
index 2cc5d416bbb5..a310cb91611c 100644
--- a/include/net/inet6_hashtables.h
+++ b/include/net/inet6_hashtables.h
@@ -134,10 +134,11 @@ struct sock *inet6_steal_sock(struct net *net, struct sk_buff *skb, int doff,
if (!reuse_sk)
return sk;
- /* We've chosen a new reuseport sock which is never refcounted. This
- * implies that sk also isn't refcounted.
- */
- WARN_ON_ONCE(*refcounted);
+ /* New reuseport sock is never refcounted; drop the old sk's ref. */
+ if (*refcounted) {
+ sock_put(sk);
+ *refcounted = false;
+ }
return reuse_sk;
}
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 6e2fe186d0dc..1589e9e7ecb2 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -474,10 +474,11 @@ struct sock *inet_steal_sock(struct net *net, struct sk_buff *skb, int doff,
if (!reuse_sk)
return sk;
- /* We've chosen a new reuseport sock which is never refcounted. This
- * implies that sk also isn't refcounted.
- */
- WARN_ON_ONCE(*refcounted);
+ /* New reuseport sock is never refcounted; drop the old sk's ref. */
+ if (*refcounted) {
+ sock_put(sk);
+ *refcounted = false;
+ }
return reuse_sk;
}
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index 5a9c826a7092..3fb114fa77de 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -113,7 +113,7 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb,
return sk;
}
#endif
- *refcounted = sk_is_refcounted(sk);
+ *refcounted = !skb_sk_is_prefetched_noref(skb);
} else {
*refcounted = true;
}
diff --git a/include/net/sock.h b/include/net/sock.h
index 51185222aac2..fb553697db83 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1870,6 +1870,7 @@ void sock_efree(struct sk_buff *skb);
#ifdef CONFIG_INET
void sock_edemux(struct sk_buff *skb);
void sock_pfree(struct sk_buff *skb);
+void sock_pfree_refcounted(struct sk_buff *skb);
static inline void skb_set_owner_edemux(struct sk_buff *skb, struct sock *sk)
{
@@ -3015,6 +3016,16 @@ static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb)
static inline bool
skb_sk_is_prefetched(struct sk_buff *skb)
{
+#ifdef CONFIG_INET
+ return skb->destructor == sock_pfree ||
+ skb->destructor == sock_pfree_refcounted;
+#else
+ return false;
+#endif /* CONFIG_INET */
+}
+
+static inline bool skb_sk_is_prefetched_noref(struct sk_buff *skb)
+{
#ifdef CONFIG_INET
return skb->destructor == sock_pfree;
#else
diff --git a/net/core/filter.c b/net/core/filter.c
index 66d83a198ea2..e473ad8c9b3c 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -7989,8 +7989,20 @@ static const struct bpf_func_proto bpf_tcp_gen_syncookie_proto = {
.arg5_type = ARG_MEM_SIZE,
};
+/*
+ * skb destructor set by TC bpf_sk_assign(), refcounted path only.
+ * Acts as a "prefetched by bpf, ref taken" marker.
+ */
+void sock_pfree_refcounted(struct sk_buff *skb)
+{
+ sock_gen_put(skb->sk);
+}
+EXPORT_SYMBOL(sock_pfree_refcounted);
+
BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
{
+ bool refcounted;
+
if (!sk || flags != 0)
return -EINVAL;
if (!skb_at_tc_ingress(skb))
@@ -7999,13 +8011,14 @@ BPF_CALL_3(bpf_sk_assign, struct sk_buff *, skb, struct sock *, sk, u64, flags)
return -ENETUNREACH;
if (sk_unhashed(sk))
return -EOPNOTSUPP;
- if (sk_is_refcounted(sk) &&
- unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+
+ refcounted = sk_is_refcounted(sk);
+ if (refcounted && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
return -ENOENT;
skb_orphan(skb);
skb->sk = sk;
- skb->destructor = sock_pfree;
+ skb->destructor = refcounted ? sock_pfree_refcounted : sock_pfree;
return 0;
}
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 73363ceedebe..e2e9739c00ac 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -195,16 +195,16 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
break;
}
- if (skb_sk_is_prefetched(skb)) {
- struct sock *sk = skb->sk;
-
- if (!sk_is_refcounted(sk)) {
- if (!refcount_inc_not_zero(&sk->sk_refcnt))
- return -ENOTCONN;
+ /*
+ * If the skb was prefetched without taking sock's ref, bump it.
+ * Skip sock_pfree-dtor'ed skbs coming from bpf_sk_assign_tcp_reqsk().
+ */
+ if (skb_sk_is_prefetched_noref(skb) && sk_fullsock(skb->sk)) {
+ if (!refcount_inc_not_zero(&skb->sk->sk_refcnt))
+ return -ENOTCONN;
- /* drop refcount on skb_orphan */
- skb->destructor = sock_edemux;
- }
+ /* drop refcount on skb_orphan */
+ skb->destructor = sock_edemux;
}
entry = kmalloc(sizeof(*entry) + route_key_size, GFP_ATOMIC);
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign()
2026-09-02 17:52 ` [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign() Michal Luczaj
@ 2026-09-02 18:38 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-09-02 18:38 UTC (permalink / raw)
To: Michal Luczaj; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] bpf_sk_assign_tcp_reqsk() stores an unreferenced pointer to a TCP_LISTEN socket inside a request socket. If the packet is queued via NF_QUEUE, the listener socket can be freed, causing a Use-After-Free when the packet is reinjected and stolen by tcp_v4_rcv().
--
commit 8e6dd77792c2f082ee2b1a593a577a3112cbcf19
Author: Michal Luczaj <mhal@rbox.co>
bpf: Fix reference leak in bpf_sk_assign()
This patch aims to fix a reference leak in bpf_sk_assign() by freezing the
decision on whether a socket is refcounted at assign time. It introduces a
new destructor sock_pfree_refcounted() and modifies __nf_queue() to bump
references for prefetched sockets without references.
> diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
> index 73363ceedebe3..e2e9739c00ac5 100644
> --- a/net/netfilter/nf_queue.c
> +++ b/net/netfilter/nf_queue.c
> @@ -195,16 +195,16 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
> break;
> }
>
> - if (skb_sk_is_prefetched(skb)) {
> - struct sock *sk = skb->sk;
> -
> - if (!sk_is_refcounted(sk)) {
> - if (!refcount_inc_not_zero(&sk->sk_refcnt))
> - return -ENOTCONN;
> + /*
> + * If the skb was prefetched without taking sock's ref, bump it.
> + * Skip sock_pfree-dtor'ed skbs coming from bpf_sk_assign_tcp_reqsk().
> + */
> + if (skb_sk_is_prefetched_noref(skb) && sk_fullsock(skb->sk)) {
[Severity: Critical]
This isn't a bug introduced by this patch, but does maintaining this behavior
for request sockets leave an unreferenced listener socket exposed to a
use-after-free?
In bpf_sk_assign_tcp_reqsk(), a request socket is allocated without taking a
reference on the listener socket (attach_listener = false), and the
unreferenced listener is stored in req->rsk_listener.
Because sk_fullsock() evaluates to false for request sockets, this check
bypasses bumping the reference count for the SKB when it is handed to
netfilter queue. While the SKB is held in userspace, it escapes the RCU
read-side critical section. If the listener socket is closed and freed
during this time, what happens when the packet is reinjected?
It appears that when tcp_v4_rcv() processes the reinjected packet, it calls
skb_steal_sock() which extracts the freed req->rsk_listener, clears the
destructor (bypassing sock_pfree), and returns the freed socket for use,
triggering a use-after-free.
Could this be triggered by a user with privileges to set up a TC BPF rule
and netfilter queue?
> + if (!refcount_inc_not_zero(&skb->sk->sk_refcnt))
> + return -ENOTCONN;
>
> - /* drop refcount on skb_orphan */
> - skb->destructor = sock_edemux;
> - }
> + /* drop refcount on skb_orphan */
> + skb->destructor = sock_edemux;
> }
>
> entry = kmalloc(sizeof(*entry) + route_key_size, GFP_ATOMIC);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902-sockmap-lookup-tcp-leak-v3-0-b998b3d49d07@rbox.co?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen()
2026-09-02 17:52 [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 1/2] bpf: Unconditionally take socket references in lookup helpers Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign() Michal Luczaj
@ 2026-09-02 18:58 ` Kuniyuki Iwashima
2026-09-02 22:52 ` Jakub Kicinski
2 siblings, 1 reply; 8+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 18:58 UTC (permalink / raw)
To: Michal Luczaj
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
John Fastabend, Stanislav Fomichev, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Willem de Bruijn,
Jakub Sitnicki, Jiayuan Chen, Joe Stringer, Ihor Solodrai,
Pablo Neira Ayuso, Florian Westphal, Phil Sutter, bpf, netdev,
linux-kernel, netfilter-devel, coreteam, Sashiko
On Wed, Sep 2, 2026 at 10:55 AM Michal Luczaj <mhal@rbox.co> wrote:
>
> This is a follow-up to Sashiko's report[1].
>
> Several BPF socket helpers acquire a socket reference only when
> sk_is_refcounted() == true, and release it, independently, by
> re-evaluating sk_is_refcounted() again at the time the release runs. TCP
> connect(AF_UNSPEC)+listen() sets SOCK_RCU_FREE on an established socket.
Due to several bug reports, we are now inclined to forbid the
buggy transformation.
https://lore.kernel.org/netdev/CANn89i+px52TtJy3S9=uHxGj3s-WueguRo1Z_4FxO=02KLmwmQ@mail.gmail.com/
> If that happens while a reference is outstanding, the release side sees
> sk_is_refcounted() == false and skips the put; the socket is leaked.
>
> unreferenced object 0xffff88811617ce00 (size 3200):
> comm "softirq", pid 0, jiffies 4294848512
> hex dump (first 32 bytes):
> 7f 00 00 01 7f 00 00 01 4d 43 02 f6 00 00 00 00 ........MC......
> 02 00 07 41 00 00 00 00 00 00 00 00 00 00 00 00 ...A............
> backtrace (crc fb5bd4c8):
> kmem_cache_alloc_noprof+0x53e/0x640
> sk_prot_alloc+0x69/0x240
> sk_clone+0x79/0x1230
> inet_csk_clone_lock+0x30/0x760
> tcp_create_openreq_child+0x34/0x2750
> tcp_v4_syn_recv_sock+0x12e/0x1080
> tcp_check_req+0x447/0x2310
> tcp_v4_rcv+0x1026/0x3c90
> ip_protocol_deliver_rcu+0x93/0x340
> ip_local_deliver_finish+0x356/0x5c0
> ip_local_deliver+0x184/0x4a0
> ip_rcv+0x4f4/0x5b0
> __netif_receive_skb_one_core+0x153/0x1b0
> process_backlog+0x28d/0x1190
> __napi_poll+0xab/0x520
> net_rx_action+0x3f0/0xca0
>
> [1]: https://lore.kernel.org/bpf/20260701235552.2B0AA1F00A3F@smtp.kernel.org/
>
> Signed-off-by: Michal Luczaj <mhal@rbox.co>
> ---
> Changes in v3:
> - Add a fix for TC bpf_sk_assign() (patch #2)
> - Clean up commit messages
> - Non-fixes went to bpf-next: https://lore.kernel.org/bpf/20260813-sockmap-lookup-get-ref-v1-0-31f5d55f44ac@rbox.co/
> - Link to v2: https://patch.msgid.link/20260803-sockmap-lookup-tcp-leak-v2-0-306e025bfe66@rbox.co
>
> Changes in v2:
> - Defer "Use sock_hold() instead of refcount_inc_not_zero() in lookup" to
> bpf-next [John]
> - Fix comment style [Sashiko]
> - Link to v1: https://patch.msgid.link/20260723-sockmap-lookup-tcp-leak-v1-0-1bbdc58ce514@rbox.co
>
> To: Alexei Starovoitov <ast@kernel.org>
> To: Daniel Borkmann <daniel@iogearbox.net>
> To: Andrii Nakryiko <andrii@kernel.org>
> To: Eduard Zingerman <eddyz87@gmail.com>
> To: Kumar Kartikeya Dwivedi <memxor@gmail.com>
> To: Martin KaFai Lau <martin.lau@linux.dev>
> To: Song Liu <song@kernel.org>
> To: Yonghong Song <yonghong.song@linux.dev>
> To: Jiri Olsa <jolsa@kernel.org>
> To: Emil Tsalapatis <emil@etsalapatis.com>
> To: John Fastabend <john.fastabend@gmail.com>
> To: Stanislav Fomichev <sdf@fomichev.me>
> To: "David S. Miller" <davem@davemloft.net>
> To: Eric Dumazet <edumazet@google.com>
> To: Jakub Kicinski <kuba@kernel.org>
> To: Paolo Abeni <pabeni@redhat.com>
> To: Simon Horman <horms@kernel.org>
> To: Kuniyuki Iwashima <kuniyu@google.com>
> To: Willem de Bruijn <willemb@google.com>
> To: Jakub Sitnicki <jakub@cloudflare.com>
> To: Jiayuan Chen <jiayuan.chen@linux.dev>
> To: Joe Stringer <joe@wand.net.nz>
> To: Ihor Solodrai <ihor.solodrai@linux.dev>
> To: Pablo Neira Ayuso <pablo@netfilter.org>
> To: Florian Westphal <fw@strlen.de>
> To: Phil Sutter <phil@nwl.cc>
> Cc: bpf@vger.kernel.org
> Cc: netdev@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: netfilter-devel@vger.kernel.org
> Cc: coreteam@netfilter.org
>
> ---
> Michal Luczaj (2):
> bpf: Unconditionally take socket references in lookup helpers
> bpf: Fix reference leak in bpf_sk_assign()
>
> include/net/inet6_hashtables.h | 9 ++++----
> include/net/inet_hashtables.h | 9 ++++----
> include/net/request_sock.h | 2 +-
> include/net/sock.h | 11 ++++++++++
> net/core/filter.c | 49 +++++++++++++++++++++++++++++-------------
> net/core/sock_map.c | 8 ++-----
> net/netfilter/nf_queue.c | 18 ++++++++--------
> 7 files changed, 67 insertions(+), 39 deletions(-)
> ---
> base-commit: ce6b9e5dd873de532cd924e2abc928220cdc2738
> change-id: 20260628-sockmap-lookup-tcp-leak-bdaba3e083c5
>
> Best regards,
> --
> Michal Luczaj <mhal@rbox.co>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen()
2026-09-02 18:58 ` [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Kuniyuki Iwashima
@ 2026-09-02 22:52 ` Jakub Kicinski
2026-09-02 23:04 ` Kuniyuki Iwashima
0 siblings, 1 reply; 8+ messages in thread
From: Jakub Kicinski @ 2026-09-02 22:52 UTC (permalink / raw)
To: Kuniyuki Iwashima
Cc: Michal Luczaj, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, John Fastabend, Stanislav Fomichev,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
bpf, netdev, linux-kernel, netfilter-devel, coreteam, Sashiko
On Wed, 2 Sep 2026 11:58:49 -0700 Kuniyuki Iwashima wrote:
> > Several BPF socket helpers acquire a socket reference only when
> > sk_is_refcounted() == true, and release it, independently, by
> > re-evaluating sk_is_refcounted() again at the time the release runs. TCP
> > connect(AF_UNSPEC)+listen() sets SOCK_RCU_FREE on an established socket.
>
> Due to several bug reports, we are now inclined to forbid the
> buggy transformation.
> https://lore.kernel.org/netdev/CANn89i+px52TtJy3S9=uHxGj3s-WueguRo1Z_4FxO=02KLmwmQ@mail.gmail.com/
Kuniyuki, would you be willing to send a patch to do that?
Not sure if anyone else is planning to, I wasn't..
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen()
2026-09-02 22:52 ` Jakub Kicinski
@ 2026-09-02 23:04 ` Kuniyuki Iwashima
2026-09-02 23:06 ` Kuniyuki Iwashima
0 siblings, 1 reply; 8+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 23:04 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michal Luczaj, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, John Fastabend, Stanislav Fomichev,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
bpf, netdev, linux-kernel, netfilter-devel, coreteam, Sashiko
On Wed, Sep 2, 2026 at 3:52 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 2 Sep 2026 11:58:49 -0700 Kuniyuki Iwashima wrote:
> > > Several BPF socket helpers acquire a socket reference only when
> > > sk_is_refcounted() == true, and release it, independently, by
> > > re-evaluating sk_is_refcounted() again at the time the release runs. TCP
> > > connect(AF_UNSPEC)+listen() sets SOCK_RCU_FREE on an established socket.
> >
> > Due to several bug reports, we are now inclined to forbid the
> > buggy transformation.
> > https://lore.kernel.org/netdev/CANn89i+px52TtJy3S9=uHxGj3s-WueguRo1Z_4FxO=02KLmwmQ@mail.gmail.com/
>
> Kuniyuki, would you be willing to send a patch to do that?
> Not sure if anyone else is planning to, I wasn't..
Sure, I'll post one like this with s/synchronize_rcu()/-EINVAL/g
https://lore.kernel.org/netdev/20260804015349.2353056-1-kuniyu@google.com/
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen()
2026-09-02 23:04 ` Kuniyuki Iwashima
@ 2026-09-02 23:06 ` Kuniyuki Iwashima
0 siblings, 0 replies; 8+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 23:06 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michal Luczaj, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, John Fastabend, Stanislav Fomichev,
David S. Miller, Eric Dumazet, Paolo Abeni, Simon Horman,
Willem de Bruijn, Jakub Sitnicki, Jiayuan Chen, Joe Stringer,
Ihor Solodrai, Pablo Neira Ayuso, Florian Westphal, Phil Sutter,
bpf, netdev, linux-kernel, netfilter-devel, coreteam, Sashiko
On Wed, Sep 2, 2026 at 4:04 PM Kuniyuki Iwashima <kuniyu@google.com> wrote:
>
> On Wed, Sep 2, 2026 at 3:52 PM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Wed, 2 Sep 2026 11:58:49 -0700 Kuniyuki Iwashima wrote:
> > > > Several BPF socket helpers acquire a socket reference only when
> > > > sk_is_refcounted() == true, and release it, independently, by
> > > > re-evaluating sk_is_refcounted() again at the time the release runs. TCP
> > > > connect(AF_UNSPEC)+listen() sets SOCK_RCU_FREE on an established socket.
> > >
> > > Due to several bug reports, we are now inclined to forbid the
> > > buggy transformation.
> > > https://lore.kernel.org/netdev/CANn89i+px52TtJy3S9=uHxGj3s-WueguRo1Z_4FxO=02KLmwmQ@mail.gmail.com/
> >
> > Kuniyuki, would you be willing to send a patch to do that?
> > Not sure if anyone else is planning to, I wasn't..
>
> Sure, I'll post one like this with s/synchronize_rcu()/-EINVAL/g
> https://lore.kernel.org/netdev/20260804015349.2353056-1-kuniyu@google.com/
and cover IPV6_ADDRFORM.
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-09-02 23:06 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 17:52 [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 1/2] bpf: Unconditionally take socket references in lookup helpers Michal Luczaj
2026-09-02 17:52 ` [PATCH bpf v3 2/2] bpf: Fix reference leak in bpf_sk_assign() Michal Luczaj
2026-09-02 18:38 ` sashiko-bot
2026-09-02 18:58 ` [PATCH bpf v3 0/2] bpf: Fix socket leaks around connect(AF_UNSPEC)+listen() Kuniyuki Iwashima
2026-09-02 22:52 ` Jakub Kicinski
2026-09-02 23:04 ` Kuniyuki Iwashima
2026-09-02 23:06 ` Kuniyuki Iwashima
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox