* [PATCH v3 bpf-next 0/3] riscv: bpf: Add BPF private stack support
@ 2026-11-06 18:15 Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 1/3] " Chen Pei
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Chen Pei @ 2026-11-06 18:15 UTC (permalink / raw)
To: ast, daniel, andrii, memxor, bjorn, puranjay, alexei.starovoitov
Cc: eddyz87, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, pulehui, pjw, palmer, aou, alex, luke.r.nels,
xi.wang, shuah, rgbi3307, guoren, bpf, linux-riscv,
linux-kselftest, linux-kernel
This series adds private-stack support to the RV64 BPF JIT.
The verifier rejects a struct_ops program that requests a private stack
with -EACCES when the JIT does not report
bpf_jit_supports_private_stack(), and sched_ext requests one for its
dispatch callback, so no sched_ext scheduler can load on riscv64 today.
Allocation, the guard value and the lifetime handling follow arm64,
x86-64 and powerpc64; the 16 byte two-copy guard layout follows arm64
and powerpc64, since x86-64 uses an 8 byte guard with a single copy.
What is specific to riscv64 is where the pointers end up:
- Every BPF stack access goes through BPF_REG_FP (S5), so pointing S5
at the top of the per-CPU area and leaving SP on the kernel stack is
all the JIT has to do. S5 is callee-saved and each subprog derives
its own, so bpf2bpf calls need no extra save and restore.
- The outgoing stack argument area stays in the kernel frame: callers
write it relative to SP while callees read it relative to RV_REG_FP.
Unlike x86-64, no register here is shared with the private-stack
frame pointer, so no verifier-side exclusion is needed and stack
arguments keep working together with a private stack. arm64 already
does the same, and patch 3 tests that combination.
- The per-CPU lookup is emitted after the tail call entry point. A
program using a private stack can still be entered as a tail call
target, at RV_TAILCALL_OFFSET, so it has to set up its own S5 there;
and since that offset is a constant added to the target's bpf_func,
everything in front of it must be the same size in every program.
The base address is materialized with absolute moves, as on arm64,
x86-64 and powerpc64, which reach the per-CPU offset through gs:, TPIDR
or r13; riscv64 has no such register, so the address of __per_cpu_offset
is loaded too. Patch 1 has the detail, including why emit_imm() is a
choice there and why the prologue length stays fixed across passes.
RV32 does not declare support and keeps the existing behaviour; the
prologue signature is unchanged, so bpf_jit_comp32.c needs no edit.
Changes since v2:
- Patch 1: PRIV_STACK_GUARD_VAL moved to bpf_jit.h.
- Patch 3: The test redone as two asm programs in
progs/verifier_stack_arg.c.
Changes since v1:
- Patch 1 commit message: guard layout credited to arm64 and powerpc64,
value alone to x86-64; __per_cpu_offset described as a choice.
- Patch 3: test_private_stack_runtime() renamed to
serial_test_private_stack_runtime(), and the stack argument skip
prints a reason.
Testing
=======
QEMU virt rv64, an 8 vCPU SMP kernel and a 1 vCPU UP kernel, both built
on the base commit below, source tree clean at the head of this series,
selftests built from these patches. There are no private-stack related
failures, and the private-stack suites logged no BUG, WARNING, unknown
opcode or guard corruption. On UP, kfunc_call/subprog,
kfunc_call/subprog_lskel and scx_simple are not run: all three fail on
the unpatched base as well.
- verifier_private_stack, struct_ops_private_stack.
- tailcalls, stack_arg, subprogs, fentry_fexit, kfunc_call,
percpu_alloc, async_stack_depth, arena_atomics, arena_ldsx.
- verifier_stack_arg, stack_arg_precision.
- may_goto_priv_stack, may_goto_far, verifier_may_goto_1,
verifier_may_goto_2.
- scx_simple.
- sched_ext runner sweep on SMP: 40 cases, 39 pass. enable_cmask fails
because its ops.enable() takes a struct scx_cmask __arena pointer and
riscv64 does not implement bpf_jit_supports_arena_args().
- patch 3's test again on x86-64 and arm64, so all three
architectures it is gated on have run it.
Chen Pei (3):
riscv: bpf: Add BPF private stack support
selftests/bpf: Enable private stack tests for riscv64
selftests/bpf: Test a private stack together with stack arguments
arch/riscv/net/bpf_jit.h | 2 +
arch/riscv/net/bpf_jit_comp64.c | 42 +++++++++-
arch/riscv/net/bpf_jit_core.c | 82 +++++++++++++++++--
.../bpf/prog_tests/struct_ops_private_stack.c | 3 +-
.../bpf/progs/verifier_private_stack.c | 41 +++++++++-
.../selftests/bpf/progs/verifier_stack_arg.c | 49 +++++++++++
6 files changed, 205 insertions(+), 14 deletions(-)
base-commit: a3c8813409f13779fec225468aba7d4067a66468
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 bpf-next 1/3] riscv: bpf: Add BPF private stack support
2026-11-06 18:15 [PATCH v3 bpf-next 0/3] riscv: bpf: Add BPF private stack support Chen Pei
@ 2026-11-06 18:15 ` Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 2/3] selftests/bpf: Enable private stack tests for riscv64 Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 3/3] selftests/bpf: Test a private stack together with stack arguments Chen Pei
2 siblings, 0 replies; 4+ messages in thread
From: Chen Pei @ 2026-11-06 18:15 UTC (permalink / raw)
To: ast, daniel, andrii, memxor, bjorn, puranjay, alexei.starovoitov
Cc: eddyz87, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, pulehui, pjw, palmer, aou, alex, luke.r.nels,
xi.wang, shuah, rgbi3307, guoren, bpf, linux-riscv,
linux-kselftest, linux-kernel
A program on a private stack keeps its BPF frames in a per-CPU area of
its own rather than on the kernel stack of whichever context entered it.
bpf_enable_priv_stack() grants that mode only once the JIT reports
bpf_jit_supports_private_stack(), which riscv64 did not: programs there
always ran on the kernel stack, and a struct_ops program that requires
one was rejected with -EACCES.
Allocate a per-CPU area of the verifier calculated depth plus a 16 byte
guard region at each end, and point BPF_REG_FP (S5) at the top of it
while leaving SP on the kernel stack. Every BPF stack access already
goes through S5, and S5 is callee-saved with each subprog deriving its
own, so nothing else in the JIT has to know about the private stack.
The outgoing stack argument area stays in the kernel frame: callers
write it relative to SP, callees relative to RV_REG_FP, which the
prologue sets to the caller's SP.
The per-CPU lookup is emitted after the tail call entry point: a program
using a private stack can still be entered as a tail call target, and
RV_TAILCALL_OFFSET is a constant added to the target's bpf_func, so what
is in front of it must be the same size in every program.
The base is materialized with absolute moves, as on arm64, x86-64 and
powerpc64: the vmalloc'd per-CPU area is beyond an auipc pair from the
JIT region, and riscv64 has no register holding the per-CPU offset, so
the address of __per_cpu_offset is loaded too. emit_imm() is a choice
there, not a requirement; emit_addr() as used by
insn_is_mov_percpu_addr() would need an extra_pass argument and an error
return in bpf_jit_build_prologue(), touching bpf_jit_comp32.c too. Both
constants are fixed before the offset convergence loop, so the prologue
length is stable across passes.
The guard value follows arm64, x86-64 and powerpc64, and the 16 byte
two-copy region at each end follows arm64 and powerpc64, since x86-64
uses an 8 byte guard with a single copy. A jited program has them
checked when it is freed, reporting the first corrupted CPU only;
failure paths release the area unchecked since the image never ran,
which also clears priv_stack_ptr for the later bpf_jit_free().
RV32 does not declare support and keeps the existing kernel stack
behaviour.
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
Reviewed-by: Pu Lehui <pulehui@huawei.com>
---
arch/riscv/net/bpf_jit.h | 2 +
arch/riscv/net/bpf_jit_comp64.c | 42 +++++++++++++++--
arch/riscv/net/bpf_jit_core.c | 82 +++++++++++++++++++++++++++++----
3 files changed, 114 insertions(+), 12 deletions(-)
diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h
index 21a190a6a6d9..dbe1ac8cd0de 100644
--- a/arch/riscv/net/bpf_jit.h
+++ b/arch/riscv/net/bpf_jit.h
@@ -14,6 +14,8 @@
#define RV_MAX_REG_ARGS 8
#define RV_EXTRA_STK_ARGS (RV_MAX_REG_ARGS - MAX_BPF_FUNC_REG_ARGS)
+#define PRIV_STACK_GUARD_SZ 16
+#define PRIV_STACK_GUARD_VAL 0xEB9F12345678eb9fULL
/* verify runtime detection extension status */
#define rv_ext_enabled(ext) \
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 371405598faa..a7a7d2f9da85 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -2134,12 +2134,34 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
return 0;
}
+static void emit_priv_stack_frame(struct rv_jit_context *ctx)
+{
+ u64 fp = (__force u64)ctx->prog->aux->priv_stack_ptr + PRIV_STACK_GUARD_SZ;
+
+ fp += round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
+ emit_imm(RV_REG_S5, fp, ctx);
+#ifdef CONFIG_SMP
+ emit_lw(RV_REG_T1, offsetof(struct thread_info, cpu), RV_REG_TP, ctx);
+ emit_imm(RV_REG_T2, (u64)__per_cpu_offset, ctx);
+ emit_sh3add(RV_REG_T1, RV_REG_T1, RV_REG_T2, ctx);
+ emit_ld(RV_REG_T1, 0, RV_REG_T1, ctx);
+ emit_add(RV_REG_S5, RV_REG_S5, RV_REG_T1, ctx);
+#endif
+}
+
void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
{
+ bool priv_stack = ctx->prog->aux->priv_stack_ptr;
int i, stack_adjust = 0, store_offset, bpf_stack_adjust = ctx->stack_arg_sz;
- bpf_stack_adjust += round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
- if (bpf_stack_adjust)
+ /*
+ * With a private stack only the outgoing stack argument area is left in
+ * this frame. It has to stay: the caller writes it relative to SP while
+ * the callee reads it relative to RV_REG_FP, the caller's SP set below.
+ */
+ if (!priv_stack)
+ bpf_stack_adjust += round_up(ctx->prog->aux->stack_depth, STACK_ALIGN);
+ if (bpf_stack_adjust || priv_stack)
mark_fp(ctx);
if (seen_reg(RV_REG_RA, ctx))
@@ -2217,7 +2239,16 @@ void bpf_jit_build_prologue(struct rv_jit_context *ctx, bool is_subprog)
emit_addi(RV_REG_FP, RV_REG_SP, stack_adjust, ctx);
- if (bpf_stack_adjust)
+ /*
+ * Point BPF_REG_FP at the top of this CPU's private stack. A program
+ * with one can still be entered as a tail call target, at
+ * RV_TAILCALL_OFFSET, so this has to come after that entry point; the
+ * offset is a constant added to the target's bpf_func, so everything in
+ * front of it must also be the same size in every program.
+ */
+ if (priv_stack)
+ emit_priv_stack_frame(ctx);
+ else if (bpf_stack_adjust)
emit_addi(RV_REG_S5, RV_REG_SP, bpf_stack_adjust, ctx);
ctx->stack_size = stack_adjust;
@@ -2320,3 +2351,8 @@ bool bpf_jit_supports_hmul(void)
{
return true;
}
+
+bool bpf_jit_supports_private_stack(void)
+{
+ return true;
+}
diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c
index f2497ec7d378..138e9fa90963 100644
--- a/arch/riscv/net/bpf_jit_core.c
+++ b/arch/riscv/net/bpf_jit_core.c
@@ -9,6 +9,7 @@
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/memory.h>
+#include <linux/percpu.h>
#include <asm/text-patching.h>
#include <asm/cfi.h>
#include "bpf_jit.h"
@@ -44,6 +45,60 @@ bool bpf_jit_needs_zext(void)
return true;
}
+static u32 priv_stack_size(struct bpf_prog *prog)
+{
+ return round_up(prog->aux->stack_depth, STACK_ALIGN) +
+ 2 * PRIV_STACK_GUARD_SZ;
+}
+
+static int alloc_priv_stack(struct bpf_prog *prog)
+{
+ u32 size = priv_stack_size(prog);
+ u32 end = (size - PRIV_STACK_GUARD_SZ) / sizeof(u64);
+ void __percpu *ptr;
+ int cpu, i;
+
+ ptr = __alloc_percpu_gfp(size, STACK_ALIGN, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ u64 *stack = per_cpu_ptr(ptr, cpu);
+
+ for (i = 0; i < PRIV_STACK_GUARD_SZ / sizeof(u64); i++) {
+ stack[i] = PRIV_STACK_GUARD_VAL;
+ stack[end + i] = PRIV_STACK_GUARD_VAL;
+ }
+ }
+ prog->aux->priv_stack_ptr = ptr;
+ return 0;
+}
+
+static void priv_stack_check_guard(struct bpf_prog *prog)
+{
+ u32 end = (priv_stack_size(prog) - PRIV_STACK_GUARD_SZ) / sizeof(u64);
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ u64 *stack = per_cpu_ptr(prog->aux->priv_stack_ptr, cpu);
+
+ if (stack[0] != PRIV_STACK_GUARD_VAL ||
+ stack[1] != PRIV_STACK_GUARD_VAL ||
+ stack[end] != PRIV_STACK_GUARD_VAL ||
+ stack[end + 1] != PRIV_STACK_GUARD_VAL) {
+ pr_err("bpf-jit: private stack overflow/underflow guard of prog %s corrupted on CPU %d\n",
+ bpf_jit_get_prog_name(prog), cpu);
+ return;
+ }
+ }
+}
+
+static void free_priv_stack(struct bpf_prog *prog)
+{
+ free_percpu(prog->aux->priv_stack_ptr);
+ prog->aux->priv_stack_ptr = NULL;
+}
+
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)
{
unsigned int prog_size = 0, extable_size = 0;
@@ -84,12 +139,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
round_up((stack_arg_cnt - RV_EXTRA_STK_ARGS) * 8, STACK_ALIGN) : 0;
ctx->prog = prog;
+ if (prog->aux->jits_use_priv_stack && alloc_priv_stack(prog))
+ goto out_free_hdr;
+
ctx->offset = kvzalloc_objs(int, prog->len);
if (!ctx->offset)
- goto out_offset;
+ goto out_free_hdr;
if (build_body(ctx, extra_pass, NULL))
- goto out_offset;
+ goto out_free_hdr;
for (i = 0; i < prog->len; i++) {
prev_ninsns += 32;
@@ -104,7 +162,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
ctx->prologue_len = ctx->ninsns;
if (build_body(ctx, extra_pass, ctx->offset))
- goto out_offset;
+ goto out_free_hdr;
ctx->epilogue_offset = ctx->ninsns;
bpf_jit_build_epilogue(ctx);
@@ -124,7 +182,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
bpf_fill_ill_insns,
bpf_prog_was_classic(prog));
if (!jit_data->ro_header)
- goto out_offset;
+ goto out_free_hdr;
/*
* Use the image(RW) for writing the JITed instructions. But also save
@@ -190,10 +248,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
ctx->offset[i] = ctx->offset[i - 1];
ctx->offset[0] = ninsns_rvoff(ctx->prologue_len);
bpf_prog_update_insn_ptrs(prog, ctx->offset, jit_data->ro_image);
-out_offset:
- kvfree(ctx->offset);
- kfree(jit_data);
- prog->aux->jit_data = NULL;
+ goto out_free_ctx;
}
return prog;
@@ -209,7 +264,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
sizeof(jit_data->header->size));
bpf_jit_binary_pack_free(jit_data->ro_header, jit_data->header);
}
- goto out_offset;
+ free_priv_stack(prog);
+out_free_ctx:
+ kvfree(ctx->offset);
+ kfree(jit_data);
+ prog->aux->jit_data = NULL;
+ return prog;
}
u64 bpf_jit_alloc_exec_limit(void)
@@ -261,6 +321,10 @@ void bpf_jit_free(struct bpf_prog *prog)
prog->bpf_func = (void *)prog->bpf_func - cfi_get_offset();
hdr = bpf_jit_binary_pack_hdr(prog);
bpf_jit_binary_pack_free(hdr, NULL);
+ if (prog->aux->priv_stack_ptr) {
+ priv_stack_check_guard(prog);
+ free_priv_stack(prog);
+ }
WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog));
}
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 bpf-next 2/3] selftests/bpf: Enable private stack tests for riscv64
2026-11-06 18:15 [PATCH v3 bpf-next 0/3] riscv: bpf: Add BPF private stack support Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 1/3] " Chen Pei
@ 2026-11-06 18:15 ` Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 3/3] selftests/bpf: Test a private stack together with stack arguments Chen Pei
2 siblings, 0 replies; 4+ messages in thread
From: Chen Pei @ 2026-11-06 18:15 UTC (permalink / raw)
To: ast, daniel, andrii, memxor, bjorn, puranjay, alexei.starovoitov
Cc: eddyz87, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, pulehui, pjw, palmer, aou, alex, luke.r.nels,
xi.wang, shuah, rgbi3307, guoren, bpf, linux-riscv,
linux-kselftest, linux-kernel
The RV64 JIT now supports the private stack. Allow the existing
verifier and struct_ops private-stack tests to run on riscv64 and add
the expected RV64 JITed instruction patterns.
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
Reviewed-by: Pu Lehui <pulehui@huawei.com>
---
.../bpf/prog_tests/struct_ops_private_stack.c | 3 +-
.../bpf/progs/verifier_private_stack.c | 41 ++++++++++++++++++-
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/struct_ops_private_stack.c b/tools/testing/selftests/bpf/prog_tests/struct_ops_private_stack.c
index 2b3ec2b79091..103179ff8733 100644
--- a/tools/testing/selftests/bpf/prog_tests/struct_ops_private_stack.c
+++ b/tools/testing/selftests/bpf/prog_tests/struct_ops_private_stack.c
@@ -6,7 +6,8 @@
#include "struct_ops_private_stack_recur.skel.h"
#include "struct_ops_private_stack_large.skel.h"
-#if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__)
+#if defined(__x86_64__) || defined(__aarch64__) || defined(__powerpc64__) || \
+ (defined(__riscv) && __riscv_xlen == 64)
static void test_private_stack(void)
{
struct struct_ops_private_stack *skel;
diff --git a/tools/testing/selftests/bpf/progs/verifier_private_stack.c b/tools/testing/selftests/bpf/progs/verifier_private_stack.c
index aa15361bcca5..3cde7b1af126 100644
--- a/tools/testing/selftests/bpf/progs/verifier_private_stack.c
+++ b/tools/testing/selftests/bpf/progs/verifier_private_stack.c
@@ -8,7 +8,8 @@
/* From include/linux/filter.h */
#define MAX_BPF_STACK 512
-#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)
+#if defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) || \
+ (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)
struct elem {
struct bpf_timer t;
@@ -42,6 +43,14 @@ __jited(" mov x0, #0x2a")
__jited(" str x0, [x27]")
__jited("...")
__jited(" ldp x25, x27, [sp], {{.*}}")
+__arch_riscv64
+__jited(" addi sp, sp, -0x20")
+__jited("...")
+__jited(" sd s5, 0x10(sp)")
+__jited("...")
+__jited(" sd a0, -0x100(s5)")
+__jited("...")
+__jited(" ld s5, 0x10(sp)")
__naked void private_stack_single_prog(void)
{
asm volatile (" \
@@ -60,6 +69,10 @@ __jited(" subq $0x8, %rsp")
__arch_arm64
__jited(" mov x25, sp")
__jited(" sub sp, sp, #0x10")
+__arch_riscv64
+__jited(" addi sp, sp, -0x30")
+__jited("...")
+__jited(" addi s5, sp, 0x10")
__naked void no_private_stack_nested(void)
{
asm volatile (" \
@@ -153,6 +166,14 @@ __jited(" str x0, [x27]")
__jited(" bl {{.*}}")
__jited("...")
__jited(" ldp x25, x27, [sp], {{.*}}")
+__arch_riscv64
+__jited(" addi sp, sp, -0x20")
+__jited("...")
+__jited(" sd s5, 0x8(sp)")
+__jited("...")
+__jited(" sd a0, -0x200(s5)")
+__jited("...")
+__jited(" ld s5, 0x8(sp)")
__naked void private_stack_nested_1(void)
{
asm volatile (" \
@@ -223,6 +244,16 @@ __jited(" bl 0x{{.*}}")
__jited(" mov x8, x0")
__jited(" mov x8, #0x0")
__jited(" ldp x25, x27, [sp], {{.*}}")
+__arch_riscv64
+__jited("func #1")
+__jited("...")
+__jited(" addi sp, sp, -0x20")
+__jited("...")
+__jited(" sd s5, 0x8(sp)")
+__jited("...")
+__jited(" sd a0, -0x200(s5)")
+__jited("...")
+__jited(" ld s5, 0x8(sp)")
__naked void private_stack_callback(void)
{
asm volatile (" \
@@ -452,6 +483,10 @@ __arch_arm64
__jited(" mrs x10, TPIDR_EL{{[0-1]}}")
__jited(" add x27, x27, x10")
__jited(" add x25, x27, {{.*}}")
+__arch_riscv64
+__jited(" addi sp, sp, -0x20")
+__jited("...")
+__jited(" sd s5, 0x8(sp)")
int private_stack_async_callback_1(void)
{
struct bpf_timer *arr_timer;
@@ -481,6 +516,10 @@ __arch_x86_64
__jited(" subq $0x100, %rsp")
__arch_arm64
__jited(" sub sp, sp, #0x100")
+__arch_riscv64
+__jited(" addi sp, sp, -0x120")
+__jited("...")
+__jited(" addi s5, sp, 0x100")
int private_stack_async_callback_2(void)
{
struct bpf_timer *arr_timer;
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 bpf-next 3/3] selftests/bpf: Test a private stack together with stack arguments
2026-11-06 18:15 [PATCH v3 bpf-next 0/3] riscv: bpf: Add BPF private stack support Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 1/3] " Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 2/3] selftests/bpf: Enable private stack tests for riscv64 Chen Pei
@ 2026-11-06 18:15 ` Chen Pei
2 siblings, 0 replies; 4+ messages in thread
From: Chen Pei @ 2026-11-06 18:15 UTC (permalink / raw)
To: ast, daniel, andrii, memxor, bjorn, puranjay, alexei.starovoitov
Cc: eddyz87, martin.lau, song, yonghong.song, jolsa, emil,
ihor.solodrai, pulehui, pjw, palmer, aou, alex, luke.r.nels,
xi.wang, shuah, rgbi3307, guoren, bpf, linux-riscv,
linux-kselftest, linux-kernel
Add two asm programs to verifier_stack_arg.c, inside the block that is
already gated on x86, arm64 and riscv64. A raw_tp program with a 64 byte
frame calls a ten-argument subprog with a 64 byte frame of its own, and
BPF_PROG_TEST_RUN runs the pair without attaching it. Args 9 and 10 stay
in the kernel frame and arg 10 points into the caller's frame, so the two
have to agree on where the outgoing area is. The callee returns what the
caller stored plus arg 9, so a wrong area shows up as a wrong retval
rather than as a crash.
Suggested-by: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
.../selftests/bpf/progs/verifier_stack_arg.c | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
index 146cdf81bbc4..e8108e768b89 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
@@ -975,6 +975,55 @@ __naked void stack_arg_precision_fallback(void)
);
}
+/* Writes both ends of its frame, then reads args 9, 10 and the frame of the caller. */
+static __naked __noinline __used
+long priv_stack_10args(long a, long b, long c, long d, long e, long f, long g, long h,
+ long i, long *j)
+{
+ asm volatile (
+ "r1 = 0;"
+ "*(u64 *)(r10 - 8) = r1;"
+ "*(u64 *)(r10 - 64) = r1;"
+ "r0 = *(u64 *)(r11 + 32);"
+ "r1 = *(u64 *)(r11 + 40);"
+ "r1 = *(u64 *)(r1 + 0);"
+ "r0 += r1;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
+/*
+ * A frame of 64 bytes in a raw_tp prog goes to the private stack.
+ * arm64 and riscv64 pass args 9 and 10 on the kernel stack. On x86-64
+ * both frames stay there as well.
+ */
+SEC("raw_tp")
+__description("stack_arg: private stack in the caller and in the callee")
+__success __retval(890)
+__naked void stack_arg_priv_stack(void)
+{
+ asm volatile (
+ "r1 = 800;"
+ "*(u64 *)(r10 - 64) = r1;"
+ "r1 = r10;"
+ "r1 += -64;"
+ "*(u64 *)(r11 - 40) = r1;"
+ "*(u64 *)(r11 - 32) = 90;"
+ "*(u64 *)(r11 - 24) = 8;"
+ "*(u64 *)(r11 - 16) = 7;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "call priv_stack_10args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
#else
/* No BTF tells about this store. The insn is what JIT has to deal with. */
--
2.50.1
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-06 18:16 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 18:15 [PATCH v3 bpf-next 0/3] riscv: bpf: Add BPF private stack support Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 1/3] " Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 2/3] selftests/bpf: Enable private stack tests for riscv64 Chen Pei
2026-11-06 18:15 ` [PATCH v3 bpf-next 3/3] selftests/bpf: Test a private stack together with stack arguments Chen Pei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®