BPF List
 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 v3 11/16] bpf: Refuse the prologue for an extension that cannot take it
Date: Sat, 17 Oct 2026 23:29:50 +0000	[thread overview]
Message-ID: <sashiko-outbox-175849@kernel.org> (raw)
In-Reply-To: <20261017230950.802052-12-alexei.starovoitov@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] bpf: unclone prologue in ext_prog_takes_prologue() misses changes_pkt_data
--

commit 5782cfd239c6517a4947d82c921ca266a19c1bde
Author: Alexei Starovoitov <ast@kernel.org>

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

This commit rejects BPF extension programs that require an unclone prologue
unless their arguments consist of a context pointer in R1 followed only by
scalars.

> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index fe59e3114d828..a8739fbd03495 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
> @@ -915,6 +934,12 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
>  			verifier_bug(env, "prologue is too long");
>  			return -EFAULT;
>  		} else if (cnt) {
> +			if (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;
> +			}
>  			new_prog = __bpf_patch_insn_data(env, 0, insn_buf, cnt,
>  							 BPF_PATCH_MOVE_TARGET);
>  			if (!new_prog)

[Severity: Critical]
When an extension program receives bpf_unclone_prologue() in
bpf_convert_ctx_accesses(), that prologue 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);
	...
}

Because this call is only synthesized during bpf_convert_ctx_accesses(),
after bpf_check_cfg() and check_attach_btf_id() -> bpf_check_attach_target()
have already run, an extension that writes to packet data via direct packet
access or bpf_dynptr_slice_rdwr() without calling a packet-changing helper
will have prog->aux->changes_pkt_data == false during target validation:

kernel/bpf/verifier.c:bpf_check_attach_target() {
	...
	tgt_changes_pkt_data = aux->func
			       ? aux->func[subprog]->aux->changes_pkt_data
			       : aux->changes_pkt_data;
	if (prog->aux->changes_pkt_data && !tgt_changes_pkt_data) {
		bpf_log(log,
			"Extension program changes packet data, while original does not\n");
		return -EINVAL;
	}
	...
}

If the target tc, lwt_xmit, or sk_skb program only reads packet data (or
clones the skb via bpf_clone_redirect() and reloads skb->data) and calls a
global subprogram taking (struct __sk_buff *skb, scalars...) with
changes_pkt_data == false, check_func_call() does not call
clear_all_pkt_pointers(env), allowing the caller to hold packet pointers or
read-only skb dynptr slices in callee-saved registers across the call.

When the extension replaces that global subprogram and runs on a cloned skb,
its bpf_unclone_prologue() calls bpf_skb_pull_data() ->
skb_ensure_writable() -> pskb_expand_head(), which reallocates and frees
skb->head while the caller still holds pointers into the old skb->head.

Could this cause a use-after-free in the caller after the extension returns,
and should an extension receiving a prologue also require the target function
to have changes_pkt_data set (and set prog->aux->changes_pkt_data for
subsequent BPF_LINK_CREATE attachments)?

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

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

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-17 23:09 [PATCH bpf-next v3 00/16] bpf: Memory safety, lifetime and locking fixes (followups) Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 01/16] bpf: Don't take a program with a target program for an iterator Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 02/16] bpf: Don't let an extension reach further into the context than its target Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 03/16] bpf: Keep values of a dead owner for sleepable programs Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 04/16] bpf: Don't take an extension for a function that runs sleepable Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 05/16] bpf: Keep the symbols of a program until it has ended Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 06/16] bpf: Use the socket of a sk_lookup test run under RCU Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 07/16] bpf, cpumap: Hold the device of frames in the queue Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 08/16] bpf: Drop packet pointers behind kfuncs writing an skb Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 09/16] bpf: Let bpf_dynptr_write() mark a global function Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 10/16] bpf: Don't take a dynptr argument for a local dynptr Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 11/16] bpf: Refuse the prologue for an extension that cannot take it Alexei Starovoitov
2026-10-17 23:29   ` sashiko-bot [this message]
2026-10-18  0:35     ` Alexei Starovoitov
2026-10-17 23:41   ` bot+bpf-ci
2026-10-17 23:09 ` [PATCH bpf-next v3 12/16] bpf: Unclone the skb for kfuncs that write through a dynptr Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 13/16] bpf: Follow the head if an skb is written from a slice Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 14/16] bpf: Use memmove() to store bytes in an skb Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 15/16] bpf: Use memmove() to store bytes in an xdp_buff Alexei Starovoitov
2026-10-17 23:09 ` [PATCH bpf-next v3 16/16] bpf: Refuse packet slices for packet-changing helpers Alexei Starovoitov
2026-10-18  0:50 ` [PATCH bpf-next v3 00/16] bpf: Memory safety, lifetime and locking fixes (followups) 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-175849@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox