From 6cc0cf0a6fb087f1979dfad5c1e13a41e57f55d7 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sat, 31 Oct 2026 05:05:58 +0000 Subject: [PATCH] selftests/bpf: Test bpf_throw() in a prog with a private stack 'Private stack, exception in main prog' and 'Private stack, exception in subprog' are fentry progs. The test loader does not attach them, and BPF_PROG_TEST_RUN of a fentry prog does not call the prog. They are loaded, but they never run. Add three raw_tp progs. BPF_PROG_TEST_RUN runs them, and the default exception callback returns the cookie of bpf_throw(): - the main prog has a private stack and throws - the main prog has a private stack, its subprog throws - the main prog has no private stack, its subprog has one and throws Without the fix of the x86-64 JIT the first two crash the kernel. The third one passes with and without it: it makes sure that only the frame of the main prog is taken into account. Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261031050558.2410721-3-alexei.starovoitov@gmail.com --- .../bpf/progs/verifier_private_stack.c | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_private_stack.c b/tools/testing/selftests/bpf/progs/verifier_private_stack.c index 3d9994a52464..bbc09f516d92 100644 --- a/tools/testing/selftests/bpf/progs/verifier_private_stack.c +++ b/tools/testing/selftests/bpf/progs/verifier_private_stack.c @@ -279,6 +279,105 @@ int private_stack_exception_sub_prog(void) return 0; } +/* + * Nothing attaches the two fentry progs above, so they are loaded, but never + * run. BPF_PROG_TEST_RUN does run a raw_tp prog. The default exception callback + * returns the cookie of bpf_throw(). + */ +SEC("raw_tp") +__description("Private stack, run, exception in main prog") +__success __retval(1) +__arch_x86_64 +__jited(" movq %rdi, -0x200(%r9)") +__jited(" movl $0x1, %edi") +__jited(" pushq %r9") +__jited("...") +__jited(" callq") +__naked void private_stack_run_exception_main_prog(void) +{ + asm volatile (" \ + r1 = 42; \ + *(u64 *)(r10 - 512) = r1; \ + r1 = 1; \ + call %[bpf_throw]; \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_throw) + : __clobber_all); +} + +__used +__naked static void subprog_throw(void) +{ + asm volatile (" \ + r1 = 2; \ + call %[bpf_throw]; \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_throw) + : __clobber_all); +} + +SEC("raw_tp") +__description("Private stack, run, exception in subprog") +__success __retval(2) +__arch_x86_64 +__jited(" movq %rdi, -0x200(%r9)") +__jited(" pushq %r9") +__jited("...") +__jited(" callq") +__jited(" popq %r9") +__naked void private_stack_run_exception_sub_prog(void) +{ + asm volatile (" \ + r1 = 42; \ + *(u64 *)(r10 - 512) = r1; \ + call subprog_throw; \ + r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + +__used +__naked static void private_stack_subprog_throw(void) +{ + asm volatile (" \ + r1 = 42; \ + *(u64 *)(r10 - 512) = r1; \ + r1 = 3; \ + call %[bpf_throw]; \ + r0 = 0; \ + exit; \ +" : + : __imm(bpf_throw) + : __clobber_all); +} + +/* Only the frame of the main prog counts, and it has nothing pushed at the call */ +SEC("raw_tp") +__description("Private stack, run, exception in subprog, no private stack in main prog") +__success __retval(3) +__arch_x86_64 +__jited(" subq $0x8, %rsp") +__jited("...") +__jited("func #1") +__jited("...") +__jited(" movq %rdi, -0x200(%r9)") +__jited(" movl $0x3, %edi") +__jited(" pushq %r9") +__naked void no_private_stack_run_exception_sub_prog(void) +{ + asm volatile (" \ + r1 = 42; \ + *(u64 *)(r10 - 8) = r1; \ + call private_stack_subprog_throw; \ + r0 = 0; \ + exit; \ +" ::: __clobber_all); +} + int glob; __noinline static void subprog2(int *val) {