]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests: bpf: Add tests for void global subprogs
authorEmil Tsalapatis <emil@etsalapatis.com>
Sat, 28 Feb 2026 18:47:59 +0000 (13:47 -0500)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 3 Mar 2026 16:47:23 +0000 (08:47 -0800)
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>
tools/testing/selftests/bpf/prog_tests/exceptions.c
tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
tools/testing/selftests/bpf/progs/exceptions.c
tools/testing/selftests/bpf/progs/exceptions_fail.c
tools/testing/selftests/bpf/progs/freplace_int_with_void.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/freplace_void.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/verifier_global_subprogs.c

index 516f4a13013cca26f9e416a75e6774b3090f64ea..84ab73e08b0e646b06d0325dc996475d1d5fc514 100644 (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)                     \
        {                                                                         \
index f29fc789c14b4c8cf02ae9a6122f9030bb1d5e59..23d933f1aec69e449c02828343290be1be6ffb9b 100644 (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();
 }
index f09cd14d8e040eaa1f7450a62236542af51fcfc9..4206f59d7b8643dcf8abb362ecdfee2704baaff3 100644 (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;
index d28ecc4ee2d03ab4222e5a953f37cf5721c97aaa..275ad6fe4a049a43583506ce2f1d22a8aa3aec07 100644 (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";
diff --git a/tools/testing/selftests/bpf/progs/freplace_int_with_void.c b/tools/testing/selftests/bpf/progs/freplace_int_with_void.c
new file mode 100644 (file)
index 0000000..cbb8f8f
--- /dev/null
@@ -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";
diff --git a/tools/testing/selftests/bpf/progs/freplace_void.c b/tools/testing/selftests/bpf/progs/freplace_void.c
new file mode 100644 (file)
index 0000000..68b114f
--- /dev/null
@@ -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";
index 20904cd2baa2fcbae5badc82877973992c2acb26..f02012a2fbaa14e13e87aab20a0a4a409d1d9a01 100644 (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";