]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests/bpf: Add test for large offset bpf-to-bpf call
authorYazhou Tang <tangyazhou518@outlook.com>
Wed, 6 May 2026 09:47:14 +0000 (17:47 +0800)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 11 May 2026 15:27:02 +0000 (08:27 -0700)
Add a selftest to verify the verifier and JIT behavior when handling
bpf-to-bpf calls with relative jump offsets exceeding the s16 boundary.

The test utilizes an inline assembly block with ".rept 32765" to generate
a massive dummy subprogram. By placing this padding between the main
program and the target subprogram, it forces the verifier to process a
bpf-to-bpf call where the imm field exceeds the s16 range.

- When JIT is enabled, it asserts that the program is successfully loaded
  and executes correctly to return the expected value. Since the fix
  does not change the JIT behavior, the test passes whether the fix is
  applied or not.
- When JIT is disabled, it also asserts that the program is successfully
  loaded and executes correctly to return the expected value 3.
  - Before the fix, the verifier rewrites the call instruction with a
    truncated offset (here 32768 -> -32768) and lets it pass. When the
    program is executed, the call instruction will go to a wrong target
    (the landing pad) instead of the intended subprogram, then return -1
    and fail.
  - After the fix, the verifier correctly handles the large offset and
    allows it to pass. The program then executes correctly to return the
    expected value 3.

Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
Acked-by: Xu Kuohai <xukuohai@huawei.com>
Link: https://lore.kernel.org/r/20260506094714.419842-4-tangyazhou@zju.edu.cn
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/prog_tests/verifier.c
tools/testing/selftests/bpf/progs/verifier_call_large_imm.c [new file with mode: 0644]

index a96b25ebff2346bd641759a025389676665714d1..06cd24e37b3f8c93570e262e0d36bd8457d1f07d 100644 (file)
@@ -22,6 +22,7 @@
 #include "verifier_bswap.skel.h"
 #include "verifier_btf_ctx_access.skel.h"
 #include "verifier_btf_unreliable_prog.skel.h"
+#include "verifier_call_large_imm.skel.h"
 #include "verifier_cfg.skel.h"
 #include "verifier_cgroup_inv_retcode.skel.h"
 #include "verifier_cgroup_skb.skel.h"
@@ -170,6 +171,7 @@ void test_verifier_bpf_trap(void)             { RUN(verifier_bpf_trap); }
 void test_verifier_bswap(void)                { RUN(verifier_bswap); }
 void test_verifier_btf_ctx_access(void)       { RUN(verifier_btf_ctx_access); }
 void test_verifier_btf_unreliable_prog(void)  { RUN(verifier_btf_unreliable_prog); }
+void test_verifier_call_large_imm(void)       { RUN(verifier_call_large_imm); }
 void test_verifier_cfg(void)                  { RUN(verifier_cfg); }
 void test_verifier_cgroup_inv_retcode(void)   { RUN(verifier_cgroup_inv_retcode); }
 void test_verifier_cgroup_skb(void)           { RUN(verifier_cgroup_skb); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c b/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
new file mode 100644 (file)
index 0000000..7998df0
--- /dev/null
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+int call_happened = 0;
+
+/*
+ * 32765 is the exact minimum number of padding instructions needed to
+ * trigger the verifier failure, because:
+ * 1. Counting the wrapper instructions around the padding block (one
+ *    "r0=0" and two "exit" instructions), the actual jump distance
+ *    evaluates to N + 3.
+ * 2. To overflow the s16 max bound (32767), we need N + 3 > 32767.
+ * Thus, N = 32765 is the exact minimum padding size required.
+ */
+static __attribute__((noinline)) void padding_subprog(void)
+{
+       asm volatile (
+       "r0 = 0;"
+       ".rept 32765;"
+       "r0 += 0;"
+       ".endr;"
+       ::: __clobber_all);
+}
+
+static __attribute__((noinline)) int target_subprog(void)
+{
+       /* Use volatile variable here to prevent optimization. */
+       volatile int magic_ret = 3;
+       return magic_ret;
+}
+
+SEC("syscall")
+__success __retval(3)
+int call_large_imm_test(void *ctx)
+{
+       /*
+        * Landing pad to handle call error on kernel without the fix,
+        * preventing kernel panic.
+        */
+       asm volatile (
+       "r0 = 0;"
+       ".rept 32768;"
+       "r0 += 0;"
+       ".endr;"
+       ::: __clobber_all);
+
+       /*
+        * The call_happened variable is 1 only when the call insn wrongly
+        * go back to the landing pad above.
+        */
+       if (call_happened == 1) {
+               /* Use volatile variable here to prevent optimization. */
+               volatile int flag = -1;
+               return flag;
+       }
+
+       call_happened = 1;
+
+       padding_subprog();
+
+       return target_subprog();
+}
+
+char LICENSE[] SEC("license") = "GPL";