summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-03-03 19:47:23 +0300
committerAlexei Starovoitov <ast@kernel.org>2026-03-03 19:47:23 +0300
commit44dd647b668984fb52b7fb73952cb1668b76ebed (patch)
treec56437d576f4102d89e5fd6de254682636b8e2e3 /tools
parent7242b0951d2cab0f5908ec768ebd3f3ca4f0884c (diff)
parent422f1efabd1389020be1c768c66aa6df40f528e7 (diff)
downloadlinux-44dd647b668984fb52b7fb73952cb1668b76ebed.tar.xz
Merge branch 'bpf-allow-void-return-type-for-global-subprogs'
Emil Tsalapatis says: ==================== bpf: Allow void return type for global subprogs Global subprogs are currently not allowed to return void. Adjust verifier logic to allow global functions with a void return type. Exception callbacks are excluded from this change, and still require a scalar return type. Adding additional logic for void globals significantly complicates check_return_code(), mainly because of how the new feature interacts with bpf_throw(). To avoid complicating the code, refactor the return value range validation logic into two separate functions, one for program exits through BPF_EXIT and one for program exits through bpf_throw(). The changes are non-functional. Also adjust existing selftests that were ensuring that global subprogs with void return types fail to verify. These tests should now load successfully. Patches 1-3 refactor check_return_code into two separate functions, one for BPF_EXIT and one for bpf_throw(). No functional changes. Patches 4-5 enable void globals in the verifier and add additional selftests. v5 -> v6 (https://lore.kernel.org/bpf/20260227154616.6846-1-emil@etsalapatis.com/) Patch 1: - Removed unused variables (kernel test robot) - Add break; on empty default to suppress warning (kernel test robot) - Added explanation on why change in switch statement for BPF_PROG_TYPE_TRACING in check_return_code is a no-op Patches 2/3: - Added From: tags to Eduard's patches and changed my Reviewed-by to Signed-off-by to properly denote patch route (submitting-patches.rst) - Renamed check_subprogram_return_code for into check_global_subprog_return_code (Eduard) Patch 4: - Remove unnecessary subprog_is_global() check in check_return_code (Eduard) - Fix indentation on patch 4 (AI) Patch 5: - Fix unused data in selftests (Eduard) v4 -> v5 (https://lore.kernel.org/bpf/20260225033356.518313-1-emil@etsalapatis.com/) - Split value validation logic from throw/nonthrow to main prog/subprog (Eduard) - Turn return_retval_range() to bool and incorporate return_32bit in retval (Eduard) - Adjust if-else chain in return_retval_range for cgroup programs into switch statement to bring it in line with the other program types v3 -> v4 (https://lore.kernel.org/bpf/20260223215046.1706110-1-emil@etsalapatis.com/) - Factor out bpf_throw() return value validation logic into its own function (Eduard) - Validate the range of bpf_throw() return values from void globals (Eduard) - Move new selftest to verifier_global_subprogs.c (Eduard) - Remove unnecessary verifier_bug_if() statements (Eduard) - Add new test to ensure void globals can call bpf_throw v2 -> v3 (https://lore.kernel.org/bpf/20260210183257.217285-1-emil@etsalapatis.com/) - Allow freplace for global programs that return void and add selftests (Kumar) v1 -> v2 (https://lore.kernel.org/bpf/20260127231414.359283-1-emil@etsalapatis.com/) - Only mark R0 as valid for non-void global functions (AI) - Rename subprog_is_void to subprog_returns_void (Kumar) - Add test that R0 is invalid after calling a void global (Alexei) - Add check to ensure exception callbacks do not return void and associated selftest Cc: Kumar Kartikeya Dwivedi <memxor@gmail.com> ==================== Link: https://patch.msgid.link/20260228184759.108145-1-emil@etsalapatis.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/testing/selftests/bpf/prog_tests/exceptions.c1
-rw-r--r--tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c24
-rw-r--r--tools/testing/selftests/bpf/progs/exceptions.c14
-rw-r--r--tools/testing/selftests/bpf/progs/exceptions_fail.c35
-rw-r--r--tools/testing/selftests/bpf/progs/freplace_int_with_void.c11
-rw-r--r--tools/testing/selftests/bpf/progs/freplace_void.c10
-rw-r--r--tools/testing/selftests/bpf/progs/test_global_func7.c2
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_global_subprogs.c19
8 files changed, 112 insertions, 4 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c
index 516f4a13013c..84ab73e08b0e 100644
--- a/tools/testing/selftests/bpf/prog_tests/exceptions.c
+++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c
@@ -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) \
{ \
diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
index f29fc789c14b..23d933f1aec6 100644
--- a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
+++ b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
@@ -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();
}
diff --git a/tools/testing/selftests/bpf/progs/exceptions.c b/tools/testing/selftests/bpf/progs/exceptions.c
index f09cd14d8e04..4206f59d7b86 100644
--- a/tools/testing/selftests/bpf/progs/exceptions.c
+++ b/tools/testing/selftests/bpf/progs/exceptions.c
@@ -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;
diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c
index 8a0fdff89927..275ad6fe4a04 100644
--- a/tools/testing/selftests/bpf/progs/exceptions_fail.c
+++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c
@@ -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() doesn't return 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
index 000000000000..cbb8f8ff2581
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/freplace_int_with_void.c
@@ -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
index 000000000000..68b114f477fe
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/freplace_void.c
@@ -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";
diff --git a/tools/testing/selftests/bpf/progs/test_global_func7.c b/tools/testing/selftests/bpf/progs/test_global_func7.c
index f182febfde3c..9e59625c1c92 100644
--- a/tools/testing/selftests/bpf/progs/test_global_func7.c
+++ b/tools/testing/selftests/bpf/progs/test_global_func7.c
@@ -12,7 +12,7 @@ void foo(struct __sk_buff *skb)
}
SEC("tc")
-__failure __msg("foo() doesn't return scalar")
+__success
int global_func7(struct __sk_buff *skb)
{
foo(skb);
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index 20904cd2baa2..f02012a2fbaa 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -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";