selftests: bpf: Add tests for void global subprogs

Add additional testing for void global functions. The tests
ensure that calls to void global functions properly keep
R0 invalid. Also make sure that exception callbacks still
require a return value.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
Link: https://lore.kernel.org/r/20260228184759.108145-6-emil@etsalapatis.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Emil Tsalapatis 2026-02-28 13:47:59 -05:00 committed by Alexei Starovoitov
parent 8446ded1e1
commit 422f1efabd
7 changed files with 111 additions and 3 deletions

View File

@ -83,6 +83,7 @@ static void test_exceptions_success(void)
RUN_SUCCESS(exception_assert_range_with, 1);
RUN_SUCCESS(exception_bad_assert_range, 0);
RUN_SUCCESS(exception_bad_assert_range_with, 10);
RUN_SUCCESS(exception_throw_from_void_global, 11);
#define RUN_EXT(load_ret, attach_err, expr, msg, after_link) \
{ \

View File

@ -347,6 +347,17 @@ static void test_func_sockmap_update(void)
prog_name, false, NULL);
}
static void test_func_replace_void(void)
{
const char *prog_name[] = {
"freplace/foo",
};
test_fexit_bpf2bpf_common("./freplace_void.bpf.o",
"./test_global_func7.bpf.o",
ARRAY_SIZE(prog_name),
prog_name, false, NULL);
}
static void test_obj_load_failure_common(const char *obj_file,
const char *target_obj_file,
const char *exp_msg)
@ -432,6 +443,15 @@ static void test_func_replace_global_func(void)
prog_name, false, NULL);
}
static void test_func_replace_int_with_void(void)
{
/* Make sure we can't freplace with the wrong type */
test_obj_load_failure_common("freplace_int_with_void.bpf.o",
"./test_global_func2.bpf.o",
"Return type UNKNOWN of test_freplace_int_with_void()"
" doesn't match type INT of global_func2()");
}
static int find_prog_btf_id(const char *name, __u32 attach_prog_fd)
{
struct bpf_prog_info info = {};
@ -597,4 +617,8 @@ void serial_test_fexit_bpf2bpf(void)
test_fentry_to_cgroup_bpf();
if (test__start_subtest("func_replace_progmap"))
test_func_replace_progmap();
if (test__start_subtest("freplace_int_with_void"))
test_func_replace_int_with_void();
if (test__start_subtest("freplace_void"))
test_func_replace_void();
}

View File

@ -109,6 +109,20 @@ int exception_tail_call(struct __sk_buff *ctx) {
return ret + 8;
}
__weak
void throw_11(void)
{
bpf_throw(11);
}
SEC("tc")
int exception_throw_from_void_global(struct __sk_buff *ctx)
{
throw_11();
return 0;
}
__noinline int exception_ext_global(struct __sk_buff *ctx)
{
volatile int ret = 0;

View File

@ -29,11 +29,15 @@ struct {
private(A) struct bpf_spin_lock lock;
private(A) struct bpf_rb_root rbtree __contains(foo, node);
__noinline void *exception_cb_bad_ret_type(u64 cookie)
__noinline void *exception_cb_bad_ret_type1(u64 cookie)
{
return NULL;
}
__noinline void exception_cb_bad_ret_type2(u64 cookie)
{
}
__noinline int exception_cb_bad_arg_0(void)
{
return 0;
@ -50,8 +54,8 @@ __noinline int exception_cb_ok_arg_small(int a)
}
SEC("?tc")
__exception_cb(exception_cb_bad_ret_type)
__failure __msg("Global function exception_cb_bad_ret_type() return value not void or scalar.")
__exception_cb(exception_cb_bad_ret_type1)
__failure __msg("Global function exception_cb_bad_ret_type1() return value not void or scalar.")
int reject_exception_cb_type_1(struct __sk_buff *ctx)
{
bpf_throw(0);
@ -85,6 +89,15 @@ int reject_exception_cb_type_4(struct __sk_buff *ctx)
return 0;
}
SEC("?tc")
__exception_cb(exception_cb_bad_ret_type2)
__failure __msg("exception cb cannot return void")
int reject_exception_cb_type_5(struct __sk_buff *ctx)
{
bpf_throw(0);
return 0;
}
__noinline
static int timer_cb(void *map, int *key, struct bpf_timer *timer)
{
@ -346,4 +359,20 @@ int reject_exception_throw_cb_diff(struct __sk_buff *ctx)
return 0;
}
__weak
void foo(void)
{
bpf_throw(1);
}
SEC("?fentry/bpf_check")
__failure __msg("At program exit the register R1 has smin=1 smax=1 should")
int reject_out_of_range_global_throw(struct __sk_buff *skb)
{
foo();
return 0;
}
char _license[] SEC("license") = "GPL";

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <linux/pkt_cls.h>
#include <bpf/bpf_helpers.h>
SEC("freplace/global_func2")
void test_freplace_int_with_void(struct __sk_buff *skb)
{
}
char _license[] SEC("license") = "GPL";

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
SEC("freplace/foo")
void test_freplace_void(struct __sk_buff *skb)
{
}
char _license[] SEC("license") = "GPL";

View File

@ -388,4 +388,23 @@ int arg_tag_dynptr(struct xdp_md *ctx)
return subprog_dynptr(&dptr);
}
__weak
void foo(void)
{
}
SEC("?tc")
__failure __msg("R0 !read_ok")
int return_from_void_global(struct __sk_buff *skb)
{
foo();
asm volatile(
"r1 = r0;"
:::
);
return 0;
}
char _license[] SEC("license") = "GPL";