selftests/bpf: Add exception tests with stack arguments

Add tests to verify that bpf_throw() correctly unwinds the stack
when the program uses outgoing stack arguments (functions with >5
args). Without the preceding x86 fix, these tests crash the kernel
on x86 due to corrupted callee-saved register restore. There is
no change for arm64 to support exception with stack arguments.

Acked-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20260517150707.289273-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Yonghong Song 2026-05-17 08:07:07 -07:00 committed by Alexei Starovoitov
parent 18a37465b0
commit 576482b55c
2 changed files with 121 additions and 0 deletions

View File

@ -85,6 +85,13 @@ static void test_exceptions_success(void)
RUN_SUCCESS(exception_bad_assert_range_with, 10);
RUN_SUCCESS(exception_throw_from_void_global, 11);
if (skel->rodata->has_stack_arg) {
RUN_SUCCESS(exception_throw_stack_arg, 56);
RUN_SUCCESS(exception_throw_after_stack_arg, 56);
RUN_SUCCESS(exception_throw_subprog_stack_arg, 56);
RUN_SUCCESS(exception_throw_subprog_after_stack_arg, 56);
}
#define RUN_EXT(load_ret, attach_err, expr, msg, after_link) \
{ \
LIBBPF_OPTS(bpf_object_open_opts, o, .kernel_log_buf = log_buf, \

View File

@ -379,4 +379,118 @@ int exception_bad_assert_range_with(struct __sk_buff *ctx)
return 1;
}
#if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) \
&& defined(__BPF_FEATURE_STACK_ARGUMENT)
const volatile bool has_stack_arg = true;
long arg1 = 1, arg2 = 2, arg3 = 3, arg4 = 4, arg5 = 5;
long arg6 = 6, arg7 = 7, arg8 = 8, arg9 = 9, arg10 = 10;
__noinline static long throwing_many_args(long a, long b, long c, long d,
long e, long f, long g, long h,
long i, long j)
{
bpf_throw(a + b + c + d + e + f + g + h + i + j);
return 0;
}
__noinline int exception_cb_sa(u64 cookie)
{
return cookie + 1;
}
SEC("tc")
__exception_cb(exception_cb_sa)
int exception_throw_stack_arg(struct __sk_buff *ctx)
{
throwing_many_args(arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8, arg9, arg10);
return 0;
}
__noinline static long no_throw_many_args(long a, long b, long c, long d,
long e, long f, long g, long h,
long i, long j)
{
return a + b + c + d + e + f + g + h + i + j;
}
SEC("tc")
__exception_cb(exception_cb_sa)
int exception_throw_after_stack_arg(struct __sk_buff *ctx)
{
long ret;
ret = no_throw_many_args(arg1, arg2, arg3, arg4, arg5,
arg6, arg7, arg8, arg9, arg10);
if (ret > 0)
bpf_throw(ret);
return 0;
}
__noinline static long subprog_throw_sa(long val)
{
throwing_many_args(val, val + 1, val + 2, val + 3, val + 4,
val + 5, val + 6, val + 7, val + 8, val + 9);
return 0;
}
SEC("tc")
__exception_cb(exception_cb_sa)
int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
{
subprog_throw_sa(arg1);
return 0;
}
__noinline static long subprog_throw_after_sa(long val)
{
long ret;
ret = no_throw_many_args(val, val + 1, val + 2, val + 3, val + 4,
val + 5, val + 6, val + 7, val + 8, val + 9);
if (ret > 0)
bpf_throw(ret);
return 0;
}
SEC("tc")
__exception_cb(exception_cb_sa)
int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
{
subprog_throw_after_sa(arg1);
return 0;
}
#else
const volatile bool has_stack_arg = false;
SEC("tc")
int exception_throw_stack_arg(struct __sk_buff *ctx)
{
return 0;
}
SEC("tc")
int exception_throw_after_stack_arg(struct __sk_buff *ctx)
{
return 0;
}
SEC("tc")
int exception_throw_subprog_stack_arg(struct __sk_buff *ctx)
{
return 0;
}
SEC("tc")
int exception_throw_subprog_after_stack_arg(struct __sk_buff *ctx)
{
return 0;
}
#endif
char _license[] SEC("license") = "GPL";