From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v2 34/35] selftests/bpf: Test progs that run out of budget
Date: Thu, 05 Nov 2026 23:19:04 +0000 [thread overview]
Message-ID: <sashiko-outbox-200365@kernel.org> (raw)
In-Reply-To: <20261105204513.2230344-35-alexei.starovoitov@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/bpf: use RUN_JITED() in serial_test_verifier_budget()
- [Medium] selftests/bpf: loop_in_main() and loop_in_main_rcu() never run out of budget
--
commit a539b7e8368514c3719738508a3c302fb0c6d907
Author: Alexei Starovoitov <ast@kernel.org>
selftests/bpf: Test progs that run out of budget
This commit adds BPF verifier selftests for programs that exhaust their loop
or recursion runtime budget, along with a test for packet pointer invalidation
after indirect calls in verifier_callx.c.
> diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> index 1424dc3036c5d..5b3eacb36a6cc 100644
> --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
[ ... ]
> @@ -235,6 +236,8 @@ void test_verifier_bounds_mix_sign_unsign(void) { RUN(verifier_bounds_mix_sign_u
> void test_verifier_bpf_get_stack(void) { RUN(verifier_bpf_get_stack); }
> void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); }
> void test_verifier_bswap(void) { RUN(verifier_bswap); }
> +/* Serial: many progs spin for 250 ms in an RCU read-side critical section, see timed_may_goto.c */
> +void serial_test_verifier_budget(void) { RUN(verifier_budget); }
[Severity: Medium]
Should serial_test_verifier_budget() use RUN_JITED(verifier_budget) instead of
RUN(verifier_budget) (matching test_verifier_callx() and
test_verifier_callx_rodata())?
When BPF JIT is disabled (for example with bpf_jit_enable=0),
is_jit_enabled() is false and prog->jit_requested is 0, while none of the
subtests in verifier_budget.c specify __load_if_JITed():
1. callback_arms_itself() has no architecture filter and passes a callback to
bpf_timer_set_callback(), which jit_subprogs() rejects with "callbacks are not
allowed in non-JITed programs".
2. Subtests with recursive subprograms (such as recursion(),
recursion_too_deep(), recursion_ends_in_the_middle(), and others) fail in
check_recursion() with -EOPNOTSUPP because bpf_prog_has_invocation_budget()
requires prog->jit_requested.
3. Subtests using unwind_goto or unwind_exit (such as loop_in_subprog_caught()
and loop_in_subprog_locked()) set prog->jit_required = 1 in
check_unwind_insns() and fail to load without JIT.
4. guard_all_regs_live() asserts per-cpu timed may_goto instructions
("r0 = &(void __percpu *)(r0)") via __xlated, which bpf_do_misc_fixups() only
emits when timed_may_goto is enabled.
5. In may_goto_stays_in_loop(), without timed may_goto in JIT,
bpf_do_misc_fixups() expands both may_goto_self and the verifier-added
may_goto at the loop head with separate stack counters via may_goto_expand().
Each time may_goto_self counts down 8M iterations and jumps to the loop head,
the outer may_goto decrements once and may_goto_self re-initializes to 8M,
resulting in 2^46 iterations in the interpreter.
> diff --git a/tools/testing/selftests/bpf/progs/verifier_budget.c b/tools/testing/selftests/bpf/progs/verifier_budget.c
> new file mode 100644
> index 0000000000000..1aa5b3eff712d
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_budget.c
> @@ -0,0 +1,1873 @@
[ ... ]
> +/* The verifier doesn't know what is in there */
> +unsigned long zero;
> +unsigned long one = 1;
[ ... ]
> +SEC("socket")
> +__description("budget: a loop in the main prog ends it")
> +__arch_x86_64
> +__arch_arm64
> +__success __retval(1)
> +__naked void loop_in_main(void)
> +{
> + asm volatile (
> + "r6 = %[zero] ll;"
> + "1:"
> + "r1 = *(u64 *)(r6 + 0);"
> + "if r1 != 0 goto 1b;"
> + "r0 = 1;"
> + "exit;"
> + :
> + : __imm_addr(zero)
> + : __clobber_all
> + );
> +}
> +
> +SEC("socket")
> +__description("budget: a loop in the main prog that holds something is left")
> +__arch_x86_64
> +__arch_arm64
> +__success __retval(1)
> +__naked void loop_in_main_rcu(void)
> +{
> + asm volatile (
> + "r6 = %[zero] ll;"
> + "call %[bpf_rcu_read_lock];"
> + "1:"
> + "r1 = *(u64 *)(r6 + 0);"
> + "if r1 != 0 goto 1b;"
> + "call %[bpf_rcu_read_unlock];"
> + "r0 = 1;"
> + "exit;"
> + :
> + : __imm_addr(zero),
> + __imm(bpf_rcu_read_lock),
> + __imm(bpf_rcu_read_unlock)
> + : __clobber_all
> + );
> +}
[Severity: Medium]
Should loop_in_main() and loop_in_main_rcu() load the address of one instead
of zero (and should loop_in_main() expect __retval(0))?
Because r6 is initialized from zero, "r1 = *(u64 *)(r6 + 0);" loads 0 on the
first trip and "if r1 != 0 goto 1b;" falls through immediately without
spinning or running out of budget.
In loop_in_main(), falling through executes "r0 = 1; exit;" instead of timing
out on the verifier-added may_goto and jumping to the budget exit ("r0 = 0;
exit;") when the main prog holds nothing. In loop_in_main_rcu(), the loop
exits via the normal branch condition on the first trip rather than timing out
via the verifier-added may_goto while holding the RCU read lock.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105204513.2230344-1-alexei.starovoitov@gmail.com?part=34
next prev parent reply other threads:[~2026-11-05 23:19 UTC|newest]
Thread overview: 66+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 20:44 [PATCH bpf-next v2 00/35] bpf: A verifier that joins states Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 01/35] bpf: Add may_goto to the loops that the verifier marks Alexei Starovoitov
2026-11-05 21:14 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 02/35] bpf: Limit depth and time of recursive subprogs at run time Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 03/35] bpf: Add union and widening of the range of a register Alexei Starovoitov
2026-11-05 21:34 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 04/35] bpf: Add join of registers and stack slots Alexei Starovoitov
2026-11-05 21:16 ` sashiko-bot
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 05/35] bpf: Add join of verifier states Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 21:40 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 06/35] bpf: Tell which arguments of a call a join must leave alone Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 07/35] bpf: Add the data structures of the main pass over the control flow graph Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 08/35] bpf: Order the insns, find what stops a loop and where may_goto can go Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 09/35] bpf: Find the registers that hold a number or a pointer to arena Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 21:40 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 10/35] bpf: Find the registers and stack slots that later checks depend on Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 11/35] bpf: Add the sets of states at an insn and the entries of functions Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 12/35] bpf: Compare states of different iterations of a loop Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 13/35] bpf: Decide which states are not joined Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 14/35] bpf: Merge the state that arrives into the set at an insn Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 15/35] bpf: Move states along edges, into loops and subprogs and out of them Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 16/35] bpf: Make the state after a call from the caller's state and an exit state Alexei Starovoitov
2026-11-05 20:44 ` [PATCH bpf-next v2 17/35] bpf: Enter and leave loops in the main pass, build flow.c Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 18/35] bpf: Tell the passes over the graph where an added may_goto goes Alexei Starovoitov
2026-11-05 22:19 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 19/35] bpf: Verify programs as a fixpoint over the control flow graph Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 22:45 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 20/35] bpf: Keep the count of a loop when a register has no closed form Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:44 ` [PATCH bpf-next v2 21/35] bpf: Verify static subprogs on their own Alexei Starovoitov
2026-11-05 21:51 ` bot+bpf-ci
2026-11-05 22:45 ` sashiko-bot
2026-11-05 20:44 ` [PATCH bpf-next v2 22/35] bpf: Allow recursive subprogs Alexei Starovoitov
2026-11-05 21:51 ` bot+bpf-ci
2026-11-05 23:09 ` sashiko-bot
2026-11-05 20:45 ` [PATCH bpf-next v2 23/35] bpf: Remove is_state_visited(), bpf_compute_scc() and iter.depth Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 24/35] bpf: Remove precision tracking Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 25/35] bpf: Remove prune points, levels of state comparison and pop_stack() Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 26/35] bpf: Update comments about paths, pruning and explored states Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 27/35] bpf: Track a pointer to one of a set of functions Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 28/35] bpf: callx reads what the subprogs whose address is taken read Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 29/35] selftests/bpf: Adjust tests to log lines that are gone Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 30/35] selftests/bpf: Adjust tests to new verdicts, messages and order Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 31/35] selftests/bpf: Test states that are not joined Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 32/35] selftests/bpf: Test static subprogs that are verified on their own Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 20:45 ` [PATCH bpf-next v2 33/35] selftests/bpf: Test loops that are counted, walked or widened Alexei Starovoitov
2026-11-05 20:45 ` [PATCH bpf-next v2 34/35] selftests/bpf: Test progs that run out of budget Alexei Starovoitov
2026-11-05 21:35 ` bot+bpf-ci
2026-11-05 23:19 ` sashiko-bot [this message]
2026-11-05 20:45 ` [PATCH bpf-next v2 35/35] selftests/bpf: Test callx of one of a set of functions Alexei Starovoitov
2026-11-05 23:17 ` sashiko-bot
2026-11-06 0:34 ` [PATCH bpf-next v2 00/35] bpf: A verifier that joins states Alexei Starovoitov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-200365@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alexei.starovoitov@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox