Re: [PATCH bpf v2 1/2] bpf, sockmap: settle sk_forward_alloc for strparser SK_PASS

[email protected] Sat, 01 Aug 2026 10:45:18 +0000
Newsgroups org.kernel.vger.bpf
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] The sk_forward_alloc deficit fix is bypassed on the backlog retry path for strparser clones, leaving the memcg page_counter underflow bug unfixed and introducing a truesize double-charge.

Pre-existing issues:
- [High] The exact same page_counter underflow vulnerability exists for non-strparser SKBs that already have an owner, but the patch restricts the fix only to strparser SKBs.
- [High] Modification of socket memory accounting (sk_forward_alloc) without holding the socket lock in the psock backlog workqueue.
--

--- Patch [1]: [PATCH bpf v2 1/2] bpf, sockmap: settle sk_forward_alloc for strparser SK_PASS ---
commit 1a3d6fb5186f9d9fdd026f8cf86dcfc1debee455
Author: Junseo Lim <[email protected]>

bpf, sockmap: settle sk_forward_alloc for strparser SK_PASS

This patch attempts to fix a memcg page_counter underflow in the strparser
SK_PASS path. It adds a zero-sized sk_rmem_schedule() top-up before
reassigning receive ownership to direct strparser clones.

> diff --git a/net/core/skmsg.c b/net/core/skmsg.c
> index 2521b643fa05d..ce5ad81602828 100644
> --- a/net/core/skmsg.c
> +++ b/net/core/skmsg.c
[ ... ]
> @@ -595,12 +596,9 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb,
>  	struct sk_msg *msg;
>  	int err;
>  
> -	/* If we are receiving on the same sock skb->sk is already assigned,
> -	 * skip memory accounting and owner transition seeing it already set
> -	 * correctly.
> -	 */
>  	if (unlikely(skb->sk == sk))
> -		return sk_psock_skb_ingress_self(psock, skb, off, len, true);
> +		return sk_psock_skb_ingress_self(psock, skb, off, len, true,
> +						 skb_bpf_strparser(skb));

[Severity: High]
Does the backlog retry path bypass this fix for strparser clones?

If a strparser clone fails in the fast path and is queued to the backlog,
the workqueue retries it. However, before the retry occurs:

    sk_psock_backlog() {
        ...
        ingress = skb_bpf_ingress(skb);
        skb_bpf_redirect_clear(skb);
        ...
    }

The call to skb_bpf_redirect_clear() unconditionally erases the
BPF_F_STRPARSER flag. When the retry reaches sk_psock_skb_ingress(),
skb_bpf_strparser(skb) will evaluate to false, passing settle_fwd_alloc = false
and bypassing the fix.

>  	msg = sk_psock_create_ingress_msg(sk, skb);
>  	if (!msg)
>  		return -EAGAIN;
[ ... ]
> @@ -618,12 +616,14 @@ static int sk_psock_skb_ingress(struct sk_psock *psock, struct sk_buff *skb,
>  	return err;
>  }
>  
> -/* Puts an skb on the ingress queue of the socket already assigned to the
> - * skb. In this case we do not need to check memory limits or skb_set_owner_r
> - * because the skb is already accounted for here.
> +/* Puts an skb on the ingress queue for psock->sk.
> + *
> + * Before assigning receive ownership to a direct strparser SK_PASS clone,
> + * settle any existing sk_forward_alloc deficit from earlier clone charges.
>   */
>  static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb,
> -				     u32 off, u32 len, bool take_ref)
> +				     u32 off, u32 len, bool take_ref,
> +				     bool settle_fwd_alloc)
>  {
>  	struct sk_msg *msg = alloc_sk_msg(GFP_ATOMIC);
>  	struct sock *sk = psock->sk;
> @@ -631,6 +631,13 @@ static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb
>  
>  	if (unlikely(!msg))
>  		return -EAGAIN;
> +
> +	if (settle_fwd_alloc &&
> +	    !sk_rmem_schedule(sk, skb, 0)) {
> +		kfree(msg);
> +		return -EAGAIN;
> +	}
> +

[Severity: High]
If the fast path fails here at sk_rmem_schedule(), does the early return
bypass skb_set_owner_r() and set up a double-charge?

By returning -EAGAIN, skb->sk remains NULL. When the skb is retried from
the backlog, sk_psock_skb_ingress() will see skb->sk != sk. This bypasses
sk_psock_skb_ingress_self() entirely, falling back to charging the full
skb->truesize again instead of 0.

Also, this is a pre-existing issue, but does modifying socket memory
accounting here in the backlog workqueue cause a data race?

The variable sk->sk_forward_alloc is updated non-atomically via functions
like sk_mem_schedule(), which require lock_sock(sk) or bh_lock_sock(sk)
to prevent lost updates. When called from the backlog workqueue
(sk_psock_backlog()), deferred ingress packets are processed with only
psock->work_mutex held:

    sk_psock_backlog() {
        mutex_lock(&psock->work_mutex);
        while ((skb = skb_peek(&psock->ingress_skb))) {
            ...
            ret = sk_psock_handle_skb(psock, skb, off, len, ingress);
            ...
    }

Concurrent TCP softirq RX or process-context TX operations could race
with sk_rmem_schedule() here, potentially corrupting sk_forward_alloc.

>  	skb_set_owner_r(skb, sk);

[Severity: High]
This isn't a bug introduced by this patch, but does the same page_counter
underflow vulnerability exist for normal TCP SKBs that already have an owner?

The patch restricts the fix (settle_fwd_alloc = true) only to SKBs with the
BPF_F_STRPARSER flag. Normal TCP SKBs passed via sk_psock_verdict_recv()
bypass the sk_rmem_schedule() top-up. 

Because they already have skb->sk == sk and skb->destructor == sock_rfree,
they undergo the exact same uncharge-recharge cycle when skb_set_owner_r()
is unconditionally called here. Could they trigger the exact same underflow?

>  
>  	/* This is used in tcp_bpf_recvmsg_parser() to determine whether the
[ ... ]

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1