Re: [PATCH bpf v2 2/2] bpf: Unconditionally take socket references in lookup helpers
Michal Luczaj <[email protected]> Mon, 3 Aug 2026 16:14:51 +0200
| Newsgroups | dev.linux.lists.sashiko-reviews,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On 8/3/26 11:35, [email protected] wrote: ... >> TC bpf_sk_assign() has the same issue; it takes a reference only when >> sk_is_refcounted() is true at assign time, but sock_pfree() (the skb >> destructor it installs) re-checks sk_is_refcounted() independently at >> release time. The same connect(AF_UNSPEC)+listen() transition leaks the >> socket here too. I'd welcome suggestions on the right way to handle this. > > [Severity: High] > This is a pre-existing issue, but as you noted, a socket reference is > permanently leaked if the socket state changes to unrefcounted between > bpf_sk_assign() and the skb destructor running. > > In net/core/filter.c:bpf_sk_assign(), the reference is conditionally acquired > and the destructor is set: > > if (sk_is_refcounted(sk) && > unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) > return -ENOENT; > > skb_orphan(skb); > skb->sk = sk; > skb->destructor = sock_pfree; > > Then in net/core/sock.c:sock_pfree(), the reference drop is independently > evaluated: > > void sock_pfree(struct sk_buff *skb) > { > struct sock *sk = skb->sk; > > if (!sk_is_refcounted(sk)) > return; > > ... > sock_gen_put(sk); > } > > If the socket transitions to SOCK_RCU_FREE via a connect(AF_UNSPEC) followed > by a listen() before the skb is freed, sock_pfree() returns early without > calling sock_gen_put(). > > Could this be addressed by either applying the same unconditional reference > counting approach to bpf_sk_assign(), or by introducing a dedicated > destructor for BPF-assigned sockets that unconditionally drops the reference > without re-evaluating sk_is_refcounted()? I was considering something like this in bpf_sk_assign(): refcounted = sk_is_refcounted(sk); if (refcounted && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) return -ENOENT; skb->destructor = refcounted ? sock_edemux : sock_pfree; which would unconditionally drop the ref in sock_edemux(), but setting a non-sock_pfree dtor affects the logic in ip_rcv_core() where we have /* Must drop socket now because of tproxy. */ if (!skb_sk_is_prefetched(skb)) skb_orphan(skb); And I'm uncertain about touching net/ipv4/ip_input.c.