All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v2 39/43] bpf: Refuse the prologue for an extension that cannot take it
Date: Sat, 17 Oct 2026 17:08:21 +0000	[thread overview]
Message-ID: <sashiko-outbox-175688@kernel.org> (raw)
In-Reply-To: <20261017163745.3341787-40-alexei.starovoitov@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] bpf: missing changes_pkt_data check and mark for extension prologue
- [Medium] bpf: bpf_convert_ctx_accesses() rejects extensions with no-op gen_prologue()
--

commit 2e35aa5750da9b2ad248d0380937d26b001912e7
Author: Alexei Starovoitov <ast@kernel.org>

bpf: Refuse the prologue for an extension that cannot take it

This commit adds ext_prog_takes_prologue() to reject BPF_PROG_TYPE_EXT
programs that write to packet data unless their first argument is
ARG_PTR_TO_CTX and any remaining register arguments are scalars.

> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index fe59e3114d828..32911d7f494cb 100644
> --- a/kernel/bpf/fixups.c
> +++ b/kernel/bpf/fixups.c
> @@ -858,6 +858,25 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
>  	return 0;
>  }
>  
> +/*
> + * The prologue takes the context from R1, and it can clobber R2-R5. An
> + * extension gets the arguments of the function that it replaces.
> + */
> +static bool ext_prog_takes_prologue(const struct bpf_verifier_env *env)
> +{
> +	const struct bpf_subprog_info *sub = &env->subprog_info[0];
> +	int i, cnt = min_t(int, sub->arg_slot_cnt, MAX_BPF_FUNC_REG_ARGS);
> +
> +	if (!cnt || sub->args[0].arg_type != ARG_PTR_TO_CTX)
> +		return false;
> +
> +	for (i = 1; i < cnt; i++)
> +		if (sub->args[i].arg_type != ARG_SCALAR)
> +			return false;
> +
> +	return true;
> +}
> +
>  /* convert load instructions that access fields of a context type into a
>   * sequence of instructions that access fields of the underlying structure:
>   *     struct __sk_buff    -> struct sk_buff
> @@ -909,6 +928,13 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
>  			verifier_bug(env, "gen_prologue is null");
>  			return -EFAULT;
>  		}
> +		if (env->seen_direct_write &&
> +		    env->prog->type == BPF_PROG_TYPE_EXT &&
> +		    !ext_prog_takes_prologue(env)) {
> +			verbose(env,
> +				"Extension writing packet data must take ctx and scalars only\n");
> +			return -EINVAL;
> +		}
>  		cnt = ops->gen_prologue(insn_buf, env->seen_direct_write,
>  					env->prog);

[Severity: Critical]
Can allowing an extension program to receive bpf_unclone_prologue() here
cause a use-after-free of skb->head in the target program when the
replaced global subprogram has changes_pkt_data == false?

When bpf_unclone_prologue() is prepended to an extension program, it emits
a call to BPF_FUNC_skb_pull_data:

net/core/filter.c:bpf_unclone_prologue() {
	...
	/* ret = bpf_skb_pull_data(skb, 0); */
	*insn++ = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1);
	*insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_2, BPF_REG_2);
	*insn++ = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
			       BPF_FUNC_skb_pull_data);
	...
}

At runtime, if skb->cloned is set (for instance, when the target program
did not perform direct packet writes itself and thus received no
bpf_unclone_prologue() at entry, or cloned the skb via
bpf_clone_redirect() before the call), bpf_skb_pull_data() calls
bpf_try_make_writable() -> skb_ensure_writable() -> pskb_expand_head(),
which reallocates skb->head and frees the old buffer.

In the target program, check_func_call() only invalidates packet pointers
and skb dynptr slices in callee-saved registers (R6-R9) if the called
subprogram has changes_pkt_data set:

kernel/bpf/verifier.c:check_func_call() {
	...
	if (env->subprog_info[subprog].changes_pkt_data)
		clear_all_pkt_pointers(env);
	...
}

However, direct packet stores (such as *(u8 *)(long)skb->data = val) and
bpf_dynptr_slice_rdwr() set env->seen_direct_write during do_check_main(),
after bpf_check_cfg() and check_attach_btf_id() ->
bpf_check_attach_target() have already run. Neither sets
prog->aux->changes_pkt_data during bpf_check_cfg(), and
bpf_convert_ctx_accesses() does not set prog->aux->changes_pkt_data or
verify that the target subprogram has tgt_changes_pkt_data set (both for
initial attach in bpf_check_attach_target() and subsequent re-attach via
bpf_tracing_prog_attach()).

If the target program holds a packet pointer (PTR_TO_PACKET,
PTR_TO_PACKET_META, or a BPF_DYNPTR_TYPE_SKB slice) in R6-R9 across a call
to a global function sub(struct __sk_buff *skb) with changes_pkt_data ==
false, and an extension replaces sub() and writes to packet data, wouldn't
the caller dereference the stale pointer in R6-R9 into the freed skb->head
after the extension's prologue runs pskb_expand_head()?

[Severity: Medium]
Also, should the ext_prog_takes_prologue() check be performed only after
ops->gen_prologue() returns cnt > 0?

In kernel/bpf/verifier.c, may_access_direct_pkt_data() sets
env->seen_direct_write = true on BPF_WRITE for BPF_PROG_TYPE_XDP,
BPF_PROG_TYPE_SK_MSG, and BPF_PROG_TYPE_CGROUP_SOCKOPT in addition to the
skb program types:

kernel/bpf/verifier.c:may_access_direct_pkt_data() {
	switch (env->prog->type) {
	...
	case BPF_PROG_TYPE_SCHED_CLS:
	case BPF_PROG_TYPE_SCHED_ACT:
	case BPF_PROG_TYPE_XDP:
	case BPF_PROG_TYPE_LWT_XMIT:
	case BPF_PROG_TYPE_SK_SKB:
	case BPF_PROG_TYPE_SK_MSG:
		if (meta && !meta->btf && meta->func_id)
			return meta->fn->pkt_access;

		if (t == BPF_WRITE)
			env->seen_direct_write = true;
		return true;

	case BPF_PROG_TYPE_CGROUP_SOCKOPT:
		if (t == BPF_WRITE)
			env->seen_direct_write = true;

		return true;
	...
}

For XDP, SK_MSG, and CGROUP_SOCKOPT targets, ops->gen_prologue() is
bpf_noop_prologue() or cg_sockopt_get_prologue(), which return 0 when
env->seen_direct_write is set. Checking ext_prog_takes_prologue() before
checking cnt > 0 rejects extensions attached to those program types (for
example, an XDP extension taking a struct bpf_dynptr * and calling
bpf_dynptr_slice_rdwr(), or taking ctx in R2) even though no prologue is
generated for them.

>  		if (cnt >= INSN_BUF_SIZE) {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261017163745.3341787-1-alexei.starovoitov@gmail.com?part=39

  reply	other threads:[~2026-10-17 17:08 UTC|newest]

Thread overview: 60+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17 16:37 [PATCH bpf-next v2 00/43] bpf: Memory safety, lifetime and locking fixes Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 01/43] bpf: use the frame mem type in devmap egress XDP Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 02/43] bpf: forbid sleeping in hash map for_each callbacks Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 03/43] bpf: don't leak the inode when token creation fails Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 04/43] bpf: don't hand out a linked element as extra element Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 05/43] bpf: reject sleepable programs for task_vma iterator Alexei Starovoitov
2026-10-17 17:16   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 06/43] bpf: forbid sleeping in bpf_find_vma() callbacks Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 07/43] bpf: look at all 64 bits of a BPF_JSET mask on 32-bit Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 08/43] bpf: don't let bpf_skb_adjust_room() use up the MSS Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 09/43] bpf: Read the probe of a perf event under event_mutex Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 10/43] bpf: Don't saturate owner_refcnt on the last unpin Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 11/43] bpf: Validate the SRH which End.DT6 uncovers in End.BPF Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 12/43] bpf: Pull the inner header for End.DT6 " Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 13/43] bpf: Update the SRH state if End.B6 fails late Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 14/43] bpf: Clear sk_refcnt of a clone before its storage is linked Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 15/43] bpf: Fix NULL deref in the sk_storage map iterator Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 16/43] bpf: Link the first element after its storage is published Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 17/43] bpf: Hold the socket in the sk_storage map iterator Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 18/43] bpf: Bound the nesting that the BTF show code follows Alexei Starovoitov
2026-10-17 16:54   ` sashiko-bot
2026-10-17 16:37 ` [PATCH bpf-next v2 19/43] bpf: Check the new program of an iterator link update Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 20/43] bpf: Fix stale offload->prog pointer after realloc Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 21/43] bpf: Don't bind a program to a netdev that goes away Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 22/43] bpf: Keep a program until its bpf_wq callback has ended Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 23/43] bpf: Count sleepable callbacks as sleepable map users Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 24/43] bpf: Check the maps that sleepable callbacks use Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 25/43] bpf: Assume no bounds for buffers behind a tail call Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 26/43] bpf: Don't take an extension for an iterator program Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 27/43] bpf: Assume no bounds for a writable tp with tail calls Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 28/43] bpf: Don't take an extension for a writable raw tracepoint program Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 29/43] bpf: Keep iterator programs off special map fields Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 30/43] bpf: Free what map_free() unlinks for sleepable readers Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 31/43] bpf: Keep values of a dead owner for sleepable programs Alexei Starovoitov
2026-10-17 17:06   ` sashiko-bot
2026-10-17 16:37 ` [PATCH bpf-next v2 32/43] bpf: Don't take an extension for a function that runs sleepable Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 33/43] bpf: Keep the symbols of a program until it has ended Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 34/43] bpf: Use the socket of a sk_lookup test run under RCU Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 35/43] bpf, cpumap: Hold the device of frames in the queue Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 36/43] bpf: Drop packet pointers behind kfuncs writing an skb Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 37/43] bpf: Let bpf_dynptr_write() mark a global function Alexei Starovoitov
2026-10-17 17:04   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 38/43] bpf: Don't take a dynptr argument for a local dynptr Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 39/43] bpf: Refuse the prologue for an extension that cannot take it Alexei Starovoitov
2026-10-17 17:08   ` sashiko-bot [this message]
2026-10-17 17:16   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 40/43] bpf: Unclone the skb for kfuncs that write through a dynptr Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 41/43] bpf: Follow the head if an skb is written from a slice Alexei Starovoitov
2026-10-17 16:37 ` [PATCH bpf-next v2 42/43] bpf: Use memmove() to store bytes in an skb Alexei Starovoitov
2026-10-17 17:16   ` bot+bpf-ci
2026-10-17 16:37 ` [PATCH bpf-next v2 43/43] bpf: Refuse packet slices for packet-changing helpers Alexei Starovoitov
2026-10-17 17:17   ` bot+bpf-ci
2026-10-17 22:10 ` [PATCH bpf-next v2 00/43] bpf: Memory safety, lifetime and locking fixes patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-175688@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=alexei.starovoitov@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.