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
next prev parent 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 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.