From: Yonghong Song <yonghong.song@linux.dev>
To: Amery Hung <ameryhung@meta.com>
Cc: bpf@vger.kernel.org, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
"Jose E . Marchesi" <jose.marchesi@oracle.com>,
kernel-team@fb.com, Martin KaFai Lau <martin.lau@kernel.org>
Subject: Re: [PATCH bpf-next 03/10] bpf: Support stack arguments for bpf functions
Date: Thu, 2 Apr 2026 13:45:58 -0700 [thread overview]
Message-ID: <0474abcd-79e6-4eaf-b459-c8e3d8cdbf8a@linux.dev> (raw)
In-Reply-To: <CAP17u3OdrYpJpx2z0gQ9OosuA-cj6etc0CwX4FyBCmM-PEn_-A@mail.gmail.com>
On 4/2/26 11:55 AM, Amery Hung wrote:
> On Wed, Apr 1, 2026 at 6:27 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>> Currently BPF functions (subprogs) are limited to 5 register arguments.
>> With [1], the compiler can emit code that passes additional arguments
>> via a dedicated stack area through bpf register
>> BPF_REG_STACK_ARG_BASE (r12), introduced in the previous patch.
>>
>> The following is an example to show how stack arguments are saved
>> and transferred between caller and callee:
>>
>> int foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
>> ...
>> bar(a1, a2, a3, a4, a5, a6, a7, a8);
>> ...
>> }
>>
>> The following is a illustration of stack allocation:
>>
>> Caller (foo) Callee (bar)
>> ============ ============
>> r12-relative stack arg area: r12-relative stack arg area:
>>
>> r12-8: [incoming arg 6] +--> r12-8: [incoming arg 6] (from caller's outgoing r12-24)
>> r12-16: [incoming arg 7] |+-> r12-16: [incoming arg 7] (from caller's outgoing r12-32)
>> ||+> r12-24: [incoming arg 8] (from caller's outgoing r12-40)
>> ---- incoming/outgoing boundary ||| ---- incoming/outgoing boundary
>> r12-24: [outgoing arg 6 to callee]+|| ...
>> r12-32: [outgoing arg 7 to callee]-+|
>> r12-40: [outgoing arg 8 to callee]--+
>>
>> The caller writes outgoing args past its own incoming area.
>> At the call site, the verifier transfers the caller's outgoing
>> slots into the callee's incoming slots.
>>
>> The verifier tracks stack arg slots separately from the regular r10
>> stack. A new 'bpf_stack_arg_state' structure mirrors the existing stack
>> slot tracking (spilled_ptr + slot_type[]) but lives in a dedicated
>> 'stack_arg_slots' array in bpf_func_state. This separation keeps the
>> stack arg area from interfering with the normal stack and frame pointer
>> (r10) bookkeeping.
>>
>> If the bpf function has more than one calls, e.g.,
>>
>> int foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
>> ...
>> bar1(a1, a2, a3, a4, a5, a6, a7, a8);
>> ...
>> bar2(a1, a2, a3, a4, a5, a6, a7, a8, a9);
>> ...
>> }
>>
>> The following is an illustration:
>>
>> Caller (foo) Callee (bar1)
>> ============ =============
>> r12-relative stack arg area: r12-relative stack arg area:
>>
>> r12-8: [incoming arg 6] +--> r12-8: [incoming arg 6] (from caller's outgoing r12-24)
>> r12-16: [incoming arg 7] |+-> r12-16: [incoming arg 7] (from caller's outgoing r12-32)
>> ||+> r12-24: [incoming arg 8] (from caller's outgoing r12-40)
>> ---- incoming/outgoing boundary ||| ---- incoming/outgoing boundary
>> r12-24: [outgoing arg 6 to callee]+|| ...
>> r12-32: [outgoing arg 7 to callee]-+|
>> r12-40: [outgoing arg 8 to callee]--+
>> ...
>> Back from bar1
>> ... Callee (bar2)
>> === =============
>> +---> r12-8: [incoming arg 6] (from caller's outgoing r12-24)
>> |+--> r12-16: [incoming arg 7] (from caller's outgoing r12-32)
>> ||+-> r12-24: [incoming arg 8] (from caller's outgoing r12-40)
>> |||+> r12-32: [incoming arg 9] (from caller's outgoing r12-48)
>> ---- incoming/outgoing boundary |||| ---- incoming/outgoing boundary
>> r12-24: [outgoing arg 6 to callee]+||| ...
>> r12-32: [outgoing arg 7 to callee]-+||
>> r12-40: [outgoing arg 8 to callee]--+|
>> r12-48: [outgoing arg 9 to callee]---+
>>
>> Global subprogs with >5 args are not yet supported.
>>
>> [1] https://github.com/llvm/llvm-project/pull/189060
>>
>> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
>> ---
>> include/linux/bpf.h | 2 +
>> include/linux/bpf_verifier.h | 15 ++-
>> kernel/bpf/btf.c | 14 +-
>> kernel/bpf/verifier.c | 248 ++++++++++++++++++++++++++++++++---
>> 4 files changed, 257 insertions(+), 22 deletions(-)
>>
>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>> index e24c4a2e95f7..a0a1e14e4394 100644
>> --- a/include/linux/bpf.h
>> +++ b/include/linux/bpf.h
>> @@ -1666,6 +1666,8 @@ struct bpf_prog_aux {
>> u32 max_pkt_offset;
>> u32 max_tp_access;
>> u32 stack_depth;
>> + u16 incoming_stack_arg_depth;
>> + u16 stack_arg_depth; /* both incoming and max outgoing of stack arguments */
>> u32 id;
>> u32 func_cnt; /* used by non-func prog as the number of func progs */
>> u32 real_func_cnt; /* includes hidden progs, only used for JIT and freeing progs */
>> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
>> index 090aa26d1c98..a260610cd1c1 100644
>> --- a/include/linux/bpf_verifier.h
>> +++ b/include/linux/bpf_verifier.h
>> @@ -268,6 +268,11 @@ struct bpf_retval_range {
>> bool return_32bit;
>> };
...
>> +
>> +/*
>> + * Write a value to the stack arg area.
>> + * off is the negative offset from the stack arg frame pointer.
>> + * Callers ensures off is 8-byte aligned and size is BPF_REG_SIZE.
>> + */
>> +static int check_stack_arg_write(struct bpf_verifier_env *env, struct bpf_func_state *state,
>> + int off, int value_regno)
>> +{
>> + int spi = (-off - 1) / BPF_REG_SIZE;
>> + struct bpf_func_state *cur;
>> + struct bpf_reg_state *reg;
>> + int i, err;
>> + u8 type;
>> +
>> + err = grow_stack_arg_slots(env, state, -off);
>> + if (err)
>> + return err;
>> +
>> + cur = env->cur_state->frame[env->cur_state->curframe];
>> + if (value_regno >= 0) {
>> + reg = &cur->regs[value_regno];
>> + state->stack_arg_slots[spi].spilled_ptr = *reg;
>> + type = is_spillable_regtype(reg->type) ? STACK_SPILL : STACK_MISC;
> It seems any spillable register can be passed to the callee, so reg
> containing ref_obj_id can be spilled to stack_arg_slots. However,
> release_reference() does not invalidate ref_obj_id in stack_arg_slots.
> Can this cause UAF like below?
>
> a6 = bpf_task_acquire(t);
> if (!a6)
> goto err;
>
> // a6 now has a valid ref_obj_id
> // foo1 calls bpf_task_release(a6);
> foo1(a1, a2, a3, a4, a5, a6);
>
> // a6 still has a valid ref_obj_id
> // foo2 dereference a6 -> UAF
> foo2(a1, a2, a3, a4, a5, a6);
>
> Since stack_arg_slots is separated from the normal stack slots, other
> types of stale registers may exist in the outgoing stack slots. For
> example:
> - stale pkt pointer after calling clear_all_pkt_pointers() in callee
> - register with inprecise nullness after calling
> mark_ptr_or_null_regs() in callee
Thanks for pointing this out. Indeed, a lot of checking are needed
for stack arguments. Looks like I need to add something like
bpf_for_each_spilled_stack_arg
in bpf_for_each_reg_in_vstate_mask for full coverage.
Will fix in the next resivion.
>
>
>> + for (i = 0; i < BPF_REG_SIZE; i++)
>> + state->stack_arg_slots[spi].slot_type[i] = type;
>> + } else {
>> + /* BPF_ST: store immediate, treat as scalar */
next prev parent reply other threads:[~2026-04-02 20:46 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-02 1:27 [PATCH bpf-next 00/10] bpf: Support stack arguments for BPF functions and kfuncs Yonghong Song
2026-04-02 1:27 ` [PATCH bpf-next 01/10] bpf: Introduce bpf register BPF_REG_STACK_ARG_BASE Yonghong Song
2026-04-02 1:27 ` [PATCH bpf-next 02/10] bpf: Reuse MAX_BPF_FUNC_ARGS for maximum number of arguments Yonghong Song
2026-04-02 1:27 ` [PATCH bpf-next 03/10] bpf: Support stack arguments for bpf functions Yonghong Song
2026-04-02 3:18 ` bot+bpf-ci
2026-04-02 14:42 ` Yonghong Song
2026-04-02 18:55 ` Amery Hung
2026-04-02 20:45 ` Yonghong Song [this message]
2026-04-02 23:38 ` Amery Hung
2026-04-03 4:05 ` Yonghong Song
2026-04-02 23:38 ` Alexei Starovoitov
2026-04-03 4:10 ` Yonghong Song
2026-04-05 21:07 ` Alexei Starovoitov
2026-04-06 4:29 ` Yonghong Song
2026-04-06 4:51 ` Alexei Starovoitov
2026-04-06 6:03 ` Yonghong Song
2026-04-06 15:17 ` Alexei Starovoitov
2026-04-06 16:19 ` Yonghong Song
2026-04-06 17:24 ` Alexei Starovoitov
2026-04-02 1:27 ` [PATCH bpf-next 04/10] bpf: Support stack arguments for kfunc calls Yonghong Song
2026-04-02 3:18 ` bot+bpf-ci
2026-04-02 14:45 ` Yonghong Song
2026-04-02 21:02 ` Amery Hung
2026-04-02 1:27 ` [PATCH bpf-next 05/10] bpf: Reject stack arguments in non-JITed programs Yonghong Song
2026-04-02 1:27 ` [PATCH bpf-next 06/10] bpf: Enable stack argument support for x86_64 Yonghong Song
2026-04-02 1:28 ` [PATCH bpf-next 07/10] bpf,x86: Implement JIT support for stack arguments Yonghong Song
2026-04-02 22:26 ` Amery Hung
2026-04-02 23:26 ` Yonghong Song
2026-04-02 23:51 ` Alexei Starovoitov
2026-04-03 4:13 ` Yonghong Song
2026-04-02 1:28 ` [PATCH bpf-next 08/10] selftests/bpf: Add tests for BPF function " Yonghong Song
2026-04-02 1:28 ` [PATCH bpf-next 09/10] selftests/bpf: Add negative test for oversized kfunc stack argument Yonghong Song
2026-04-02 1:28 ` [PATCH bpf-next 10/10] selftests/bpf: Add verifier tests for stack argument validation Yonghong Song
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=0474abcd-79e6-4eaf-b459-c8e3d8cdbf8a@linux.dev \
--to=yonghong.song@linux.dev \
--cc=ameryhung@meta.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=jose.marchesi@oracle.com \
--cc=kernel-team@fb.com \
--cc=martin.lau@kernel.org \
/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.