From 1ddffb57b36239e84b8224b12e34b87f6031b29c Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sat, 31 Oct 2026 21:55:35 +0000 Subject: [PATCH] bpf: Emit timed may_goto for JITed progs only The timed form of may_goto calls arch_bpf_timed_may_goto(). This function takes its argument and returns its result in BPF_REG_AX, in a register of the cpu. Only JITed code can call it. The verifier emits the timed form when the arch has a JIT that supports it. It does not look at the prog. With CONFIG_BPF_JIT_ALWAYS_ON=n the interpreter can run such a prog: the JIT is off, or it fails. The interpreter calls the function like a helper. The function takes what the C code left in that register for a pointer, and bpf_check_timed_may_goto() reads and writes through it. Emit the timed form only when the prog asks for a JIT, and make the JIT mandatory for such a prog. All other progs get the form that counts down from BPF_MAX_LOOPS. One test of verifier_may_goto_1 shows the insns of the timed form. Load it only when progs are JITed. Fixes: e723608bf428 ("bpf: Add verifier support for timed may_goto") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261031215543.682284-2-alexei.starovoitov@gmail.com --- kernel/bpf/fixups.c | 9 ++++++--- tools/testing/selftests/bpf/progs/verifier_may_goto_1.c | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c index 41bc37e205e0..bb6b57f5e144 100644 --- a/kernel/bpf/fixups.c +++ b/kernel/bpf/fixups.c @@ -1823,6 +1823,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) struct bpf_subprog_info *subprogs = env->subprog_info; u16 stack_depth = subprogs[cur_subprog].stack_depth; u16 stack_depth_extra = 0; + /* arch_bpf_timed_may_goto() takes and returns AX in a cpu register: for JITs only */ + bool timed_may_goto = prog->jit_requested && bpf_jit_supports_timed_may_goto(); if (env->seen_exception && !env->exception_callback_subprog) { struct bpf_insn *patch = insn_buf; @@ -2078,7 +2080,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) goto next_insn; } - if (bpf_is_may_goto_insn(insn) && bpf_jit_supports_timed_may_goto()) { + if (bpf_is_may_goto_insn(insn) && timed_may_goto) { int stack_off_cnt = -stack_depth - 16; /* * AX is used as an argument to pass in stack_off_cnt @@ -2108,6 +2110,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) * after subtraction, rinse and repeat. */ stack_depth_extra = 16; + prog->jit_required = 1; cnt = may_goto_expand(insn_buf, insn->off, stack_off_cnt, tail, ARRAY_SIZE(tail)); @@ -2730,7 +2733,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) env->prog->aux->stack_depth = subprogs[0].stack_depth; for (i = 0; i < env->subprog_cnt; i++) { - int delta = bpf_jit_supports_timed_may_goto() ? 2 : 1; + int delta = timed_may_goto ? 2 : 1; int subprog_start = subprogs[i].start; int stack_slots = subprogs[i].stack_extra / 8; int slots = delta, cnt = 0; @@ -2744,7 +2747,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env) } stack_depth = subprogs[i].stack_depth; - if (bpf_jit_supports_timed_may_goto()) { + if (timed_may_goto) { insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_FP, -stack_depth, BPF_MAX_TIMED_LOOPS); insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_FP, -stack_depth + 8, 0); diff --git a/tools/testing/selftests/bpf/progs/verifier_may_goto_1.c b/tools/testing/selftests/bpf/progs/verifier_may_goto_1.c index faf9eec0a63e..c19b0522df61 100644 --- a/tools/testing/selftests/bpf/progs/verifier_may_goto_1.c +++ b/tools/testing/selftests/bpf/progs/verifier_may_goto_1.c @@ -82,6 +82,7 @@ __naked void may_goto_batch_1(void) SEC("raw_tp") __description("may_goto batch with offsets 2/0") +__load_if_JITed() __arch_x86_64 __arch_s390x __arch_arm64