Re: [PATCH v3 bpf/net 6/6] sockmap: Fix broken memory accounting for UDP.
From: Kuniyuki Iwashima <kuniyu@google.com>
Date: 2026-02-19 18:29:50
Also in:
bpf
On Thu, Feb 19, 2026 at 10:11 AM [off-list ref] wrote:
quoted
diff --git a/net/core/skmsg.c b/net/core/skmsg.c --- a/net/core/skmsg.c +++ b/net/core/skmsg.c[ ... ]quoted
@@ -576,6 +577,7 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, u32 off, u32 len, gfp_t gfp_flags, bool take_ref) { struct sock *sk = psock->sk; + bool is_udp = sk_is_udp(sk); struct sk_msg *msg; int err = -EAGAIN;@@ -583,12 +585,15 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb, if (!msg) goto out; - if (skb->sk != sk) { + if (is_udp) + spin_lock_bh(&sk->sk_receive_queue.lock); + + if (skb->sk != sk || is_udp) { if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf) - goto free; + goto unlock; if (!sk_rmem_schedule(sk, skb, skb->truesize)) - goto free; + goto unlock; }[ ... ]quoted
skb_set_owner_r(skb, sk);^^^ If sk_psock_skb_ingress_enqueue() fails after this point (for example, skb_to_sgvec() fails and skb_linearize() also fails under GFP_ATOMIC memory pressure), the error path frees msg and returns -EAGAIN, but the skb is kept with skb->destructor set to udp_sock_rfree. The caller sk_psock_verdict_apply() then queues this skb onto psock->ingress_skb for retry via sk_psock_backlog(). On retry, sk_psock_handle_skb() calls sk_psock_skb_ingress() again. The function acquires sk->sk_receive_queue.lock, then calls skb_set_owner_r() which calls skb_orphan(). skb_orphan() invokes the current skb->destructor, which is now udp_sock_rfree(): sk_psock_skb_ingress() spin_lock_bh(&sk->sk_receive_queue.lock) <-- held skb_set_owner_r(skb, sk) skb_orphan(skb) skb->destructor(skb) [= udp_sock_rfree] spin_lock_bh(&sk->sk_receive_queue.lock) <-- deadlock Could this self-deadlock on the receive queue lock?
Oh exactly... Will short-circuit to sk_psock_skb_ingress_enqueue() in that case.
quoted
+ if (is_udp) { + skb->destructor = udp_sock_rfree; + spin_unlock_bh(&sk->sk_receive_queue.lock); + } + err = sk_psock_skb_ingress_enqueue(skb, off, len, psock, sk, msg, take_ref); if (err < 0) goto free; out: return err; + +unlock: + if (is_udp) + spin_unlock_bh(&sk->sk_receive_queue.lock); free: kfree(msg); goto out;quoted
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c[ ... ]quoted
+void udp_sock_rfree(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + + spin_lock_bh(&sk->sk_receive_queue.lock); + sock_rfree(skb); + spin_unlock_bh(&sk->sk_receive_queue.lock); +}--- AI reviewed your patch. Please fix the bug or email reply why it's not a bug. See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22193327667 AI-authorship-score: low AI-authorship-explanation: The commit demonstrates deep understanding of UDP memory accounting internals and references a specific syzbot report, consistent with an experienced kernel networking developer. issues-found: 1 issue-severity-score: high issue-severity-explanation: Self-deadlock on sk->sk_receive_queue.lock when sk_psock_skb_ingress is retried after enqueue failure for UDP, causing a system hang.