* [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs
@ 2025-05-08 20:40 Paul Chaignon
2025-05-08 22:31 ` Alexei Starovoitov
0 siblings, 1 reply; 5+ messages in thread
From: Paul Chaignon @ 2025-05-08 20:40 UTC (permalink / raw)
To: bpf; +Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Throughout the verifier's logic, there are multiple checks for
inconsistent states that should never happen and would indicate a
verifier bug. These bugs are typically logged in the verifier logs and
sometimes preceded by a WARN_ONCE.
This patch reworks these checks to consistently emit a verifier log AND
a warning. The consistent use of WARN_ONCE should help fuzzers (ex.
syzkaller) expose any situation where they are actually able to reach
one of those buggy verifier states.
Signed-off-by: Paul Chaignon <paul.chaignon@gmail.com>
---
kernel/bpf/verifier.c | 63 +++++++++++++++++++++++--------------------
1 file changed, 34 insertions(+), 29 deletions(-)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 99aa2c890e7b..521acbba2fda 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -196,6 +196,12 @@ struct bpf_verifier_stack_elem {
#define BPF_PRIV_STACK_MIN_SIZE 64
+#define verifier_bug(env, fmt, args...) \
+ do { \
+ WARN_ONCE(1, "verifier bug: " fmt, ##args); \
+ verbose(env, "verifier bug: " fmt, ##args); \
+ } while (0)
+
static int acquire_reference(struct bpf_verifier_env *env, int insn_idx);
static int release_reference_nomark(struct bpf_verifier_state *state, int ref_obj_id);
static int release_reference(struct bpf_verifier_env *env, int ref_obj_id);
@@ -1924,8 +1930,7 @@ static struct bpf_verifier_state *get_loop_entry(struct bpf_verifier_env *env,
while (topmost && topmost->loop_entry) {
if (steps++ > st->dfs_depth) {
- WARN_ONCE(true, "verifier bug: infinite loop in get_loop_entry\n");
- verbose(env, "verifier bug: infinite loop in get_loop_entry()\n");
+ verifier_bug(env, "infinite loop in get_loop_entry\n");
return ERR_PTR(-EFAULT);
}
topmost = topmost->loop_entry;
@@ -3460,9 +3465,9 @@ static int mark_reg_read(struct bpf_verifier_env *env,
if (writes && state->live & REG_LIVE_WRITTEN)
break;
if (parent->live & REG_LIVE_DONE) {
- verbose(env, "verifier BUG type %s var_off %lld off %d\n",
- reg_type_str(env, parent->type),
- parent->var_off.value, parent->off);
+ verifier_bug(env, "type %s var_off %lld off %d\n",
+ reg_type_str(env, parent->type),
+ parent->var_off.value, parent->off);
return -EFAULT;
}
/* The first condition is more likely to be true than the
@@ -6562,13 +6567,13 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
next_insn = i + insn[i].imm + 1;
sidx = find_subprog(env, next_insn);
if (sidx < 0) {
- WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
- next_insn);
+ verifier_bug(env, "No program starts at insn %d\n",
+ next_insn);
return -EFAULT;
}
if (subprog[sidx].is_async_cb) {
if (subprog[sidx].has_tail_call) {
- verbose(env, "verifier bug. subprog has tail_call and async cb\n");
+ verifier_bug(env, "subprog has tail_call and async cb\n");
return -EFAULT;
}
/* async callbacks don't increase bpf prog stack size unless called directly */
@@ -6676,8 +6681,8 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env,
subprog = find_subprog(env, start);
if (subprog < 0) {
- WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
- start);
+ verifier_bug(env, "No program starts at insn %d\n",
+ start);
return -EFAULT;
}
return env->subprog_info[subprog].stack_depth;
@@ -7984,7 +7989,7 @@ static int check_stack_range_initialized(
slot = -i - 1;
spi = slot / BPF_REG_SIZE;
if (state->allocated_stack <= slot) {
- verbose(env, "verifier bug: allocated_stack too small\n");
+ verbose(env, "allocated_stack too small\n");
return -EFAULT;
}
@@ -8413,7 +8418,7 @@ static int process_timer_func(struct bpf_verifier_env *env, int regno,
return -EINVAL;
}
if (meta->map_ptr) {
- verbose(env, "verifier bug. Two map pointers in a timer helper\n");
+ verifier_bug(env, "Two map pointers in a timer helper\n");
return -EFAULT;
}
meta->map_uid = reg->map_uid;
@@ -10285,8 +10290,8 @@ static int setup_func_entry(struct bpf_verifier_env *env, int subprog, int calls
}
if (state->frame[state->curframe + 1]) {
- verbose(env, "verifier bug. Frame %d already allocated\n",
- state->curframe + 1);
+ verifier_bug(env, "Frame %d already allocated\n",
+ state->curframe + 1);
return -EFAULT;
}
@@ -10400,8 +10405,8 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
if (err)
return err;
} else {
- bpf_log(log, "verifier bug: unrecognized arg#%d type %d\n",
- i, arg->arg_type);
+ verifier_bug(env, "unrecognized arg#%d type %d\n",
+ i, arg->arg_type);
return -EFAULT;
}
}
@@ -10464,13 +10469,13 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
env->subprog_info[subprog].is_cb = true;
if (bpf_pseudo_kfunc_call(insn) &&
!is_callback_calling_kfunc(insn->imm)) {
- verbose(env, "verifier bug: kfunc %s#%d not marked as callback-calling\n",
- func_id_name(insn->imm), insn->imm);
+ verifier_bug(env, "kfunc %s#%d not marked as callback-calling\n",
+ func_id_name(insn->imm), insn->imm);
return -EFAULT;
} else if (!bpf_pseudo_kfunc_call(insn) &&
!is_callback_calling_function(insn->imm)) { /* helper */
- verbose(env, "verifier bug: helper %s#%d not marked as callback-calling\n",
- func_id_name(insn->imm), insn->imm);
+ verifier_bug(env, "helper %s#%d not marked as callback-calling\n",
+ func_id_name(insn->imm), insn->imm);
return -EFAULT;
}
@@ -10523,7 +10528,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
target_insn = *insn_idx + insn->imm + 1;
subprog = find_subprog(env, target_insn);
if (subprog < 0) {
- verbose(env, "verifier bug. No program starts at insn %d\n", target_insn);
+ verifier_bug(env, "No program starts at insn %d\n", target_insn);
return -EFAULT;
}
@@ -11124,7 +11129,7 @@ static int check_bpf_snprintf_call(struct bpf_verifier_env *env,
err = fmt_map->ops->map_direct_value_addr(fmt_map, &fmt_addr,
fmt_map_off);
if (err) {
- verbose(env, "verifier bug\n");
+ verbose(env, "failed to retrieve map value address\n");
return -EFAULT;
}
fmt = (char *)(long)fmt_addr + fmt_map_off;
@@ -19689,8 +19694,8 @@ static int do_check(struct bpf_verifier_env *env)
return err;
break;
} else {
- if (WARN_ON_ONCE(env->cur_state->loop_entry)) {
- verbose(env, "verifier bug: env->cur_state->loop_entry != NULL\n");
+ if (unlikely(env->cur_state->loop_entry)) {
+ verifier_bug(env, "env->cur_state->loop_entry != NULL\n");
return -EFAULT;
}
do_print_state = true;
@@ -20750,8 +20755,8 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
if (bpf_pseudo_kfunc_call(&insn))
continue;
- if (WARN_ON(load_reg == -1)) {
- verbose(env, "verifier bug. zext_dst is set, but no reg is defined\n");
+ if (unlikely(load_reg == -1)) {
+ verifier_bug(env, "zext_dst is set, but no reg is defined\n");
return -EFAULT;
}
@@ -21071,8 +21076,8 @@ static int jit_subprogs(struct bpf_verifier_env *env)
*/
subprog = find_subprog(env, i + insn->imm + 1);
if (subprog < 0) {
- WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
- i + insn->imm + 1);
+ verifier_bug(env, "No program starts at insn %d\n",
+ i + insn->imm + 1);
return -EFAULT;
}
/* temporarily remember subprog id inside insn instead of
@@ -22433,7 +22438,7 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
continue;
/* We need two slots in case timed may_goto is supported. */
if (stack_slots > slots) {
- verbose(env, "verifier bug: stack_slots supports may_goto only\n");
+ verifier_bug(env, "stack_slots supports may_goto only\n");
return -EFAULT;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs
2025-05-08 20:40 [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs Paul Chaignon
@ 2025-05-08 22:31 ` Alexei Starovoitov
2025-05-09 8:26 ` Daniel Borkmann
0 siblings, 1 reply; 5+ messages in thread
From: Alexei Starovoitov @ 2025-05-08 22:31 UTC (permalink / raw)
To: Paul Chaignon; +Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
On Thu, May 8, 2025 at 1:40 PM Paul Chaignon <paul.chaignon@gmail.com> wrote:
>
> Throughout the verifier's logic, there are multiple checks for
> inconsistent states that should never happen and would indicate a
> verifier bug. These bugs are typically logged in the verifier logs and
> sometimes preceded by a WARN_ONCE.
>
> This patch reworks these checks to consistently emit a verifier log AND
> a warning. The consistent use of WARN_ONCE should help fuzzers (ex.
> syzkaller) expose any situation where they are actually able to reach
> one of those buggy verifier states.
No. We cannot do it.
WARN_ONCE is for kernel level issues.
In some configs use panic_on_warn=1 too.
Whereas a verifier bug is contained within a verifier.
It will not bring the kernel down.
We should remove most of the existing WARN_ONCE instead.
Potentially replace them with pr_info_once().
pw-bot: cr
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs
2025-05-08 22:31 ` Alexei Starovoitov
@ 2025-05-09 8:26 ` Daniel Borkmann
2025-05-09 17:58 ` Alexei Starovoitov
0 siblings, 1 reply; 5+ messages in thread
From: Daniel Borkmann @ 2025-05-09 8:26 UTC (permalink / raw)
To: Alexei Starovoitov, Paul Chaignon
Cc: bpf, Alexei Starovoitov, Andrii Nakryiko
On 5/9/25 12:31 AM, Alexei Starovoitov wrote:
> On Thu, May 8, 2025 at 1:40 PM Paul Chaignon <paul.chaignon@gmail.com> wrote:
>>
>> Throughout the verifier's logic, there are multiple checks for
>> inconsistent states that should never happen and would indicate a
>> verifier bug. These bugs are typically logged in the verifier logs and
>> sometimes preceded by a WARN_ONCE.
>>
>> This patch reworks these checks to consistently emit a verifier log AND
>> a warning. The consistent use of WARN_ONCE should help fuzzers (ex.
>> syzkaller) expose any situation where they are actually able to reach
>> one of those buggy verifier states.
>
> No. We cannot do it.
> WARN_ONCE is for kernel level issues.
> In some configs use panic_on_warn=1 too.
> Whereas a verifier bug is contained within a verifier.
> It will not bring the kernel down.
Agree.
> We should remove most of the existing WARN_ONCE instead.
> Potentially replace them with pr_info_once().
Just a thought, maybe one potential avenue could be to have an equivalent of
CONFIG_DEBUG_NET which we make a hard dependency of CONFIG_DEBUG_KERNEL so that
/noone/ enables this anywhere in production, and then fuzzers could use it
to their advantage. The default case for a DEBUG_BPF_WARN_ONCE would then fall
to BUILD_BUG_ON_INVALID() which lets compiler check validity but not generate
code.
Cheers,
Daniel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs
2025-05-09 8:26 ` Daniel Borkmann
@ 2025-05-09 17:58 ` Alexei Starovoitov
2025-05-14 11:41 ` Paul Chaignon
0 siblings, 1 reply; 5+ messages in thread
From: Alexei Starovoitov @ 2025-05-09 17:58 UTC (permalink / raw)
To: Daniel Borkmann; +Cc: Paul Chaignon, bpf, Alexei Starovoitov, Andrii Nakryiko
On Fri, May 9, 2025 at 1:26 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On 5/9/25 12:31 AM, Alexei Starovoitov wrote:
> > On Thu, May 8, 2025 at 1:40 PM Paul Chaignon <paul.chaignon@gmail.com> wrote:
> >>
> >> Throughout the verifier's logic, there are multiple checks for
> >> inconsistent states that should never happen and would indicate a
> >> verifier bug. These bugs are typically logged in the verifier logs and
> >> sometimes preceded by a WARN_ONCE.
> >>
> >> This patch reworks these checks to consistently emit a verifier log AND
> >> a warning. The consistent use of WARN_ONCE should help fuzzers (ex.
> >> syzkaller) expose any situation where they are actually able to reach
> >> one of those buggy verifier states.
> >
> > No. We cannot do it.
> > WARN_ONCE is for kernel level issues.
> > In some configs use panic_on_warn=1 too.
> > Whereas a verifier bug is contained within a verifier.
> > It will not bring the kernel down.
>
> Agree.
>
> > We should remove most of the existing WARN_ONCE instead.
> > Potentially replace them with pr_info_once().
>
> Just a thought, maybe one potential avenue could be to have an equivalent of
> CONFIG_DEBUG_NET which we make a hard dependency of CONFIG_DEBUG_KERNEL so that
> /noone/ enables this anywhere in production, and then fuzzers could use it
> to their advantage. The default case for a DEBUG_BPF_WARN_ONCE would then fall
> to BUILD_BUG_ON_INVALID() which lets compiler check validity but not generate
> code.
Good idea.
but I'm not a fan of the new kconfig.
I'd rather combine some of them.
Like make CONFIG_BPF_JIT depend on CONFIG_BPF_SYSCALL.
That would remove a ton of empty static inline helpers.
For this case doing WARN_ON only when IS_ENABLED(CONFIG_DEBUG_KERNEL)
should be fine.
I guess we can introduce BPF_WARN_ONCE() and friends family of macros
and use them everywhere in kernel/bpf/ where the warning is not
harmful to the kernel ?
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs
2025-05-09 17:58 ` Alexei Starovoitov
@ 2025-05-14 11:41 ` Paul Chaignon
0 siblings, 0 replies; 5+ messages in thread
From: Paul Chaignon @ 2025-05-14 11:41 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Daniel Borkmann, bpf, Alexei Starovoitov, Andrii Nakryiko
On Fri, May 09, 2025 at 10:58:00AM -0700, Alexei Starovoitov wrote:
> On Fri, May 9, 2025 at 1:26 AM Daniel Borkmann <daniel@iogearbox.net> wrote:
> >
> > On 5/9/25 12:31 AM, Alexei Starovoitov wrote:
> > > On Thu, May 8, 2025 at 1:40 PM Paul Chaignon <paul.chaignon@gmail.com> wrote:
> > >>
> > >> Throughout the verifier's logic, there are multiple checks for
> > >> inconsistent states that should never happen and would indicate a
> > >> verifier bug. These bugs are typically logged in the verifier logs and
> > >> sometimes preceded by a WARN_ONCE.
> > >>
> > >> This patch reworks these checks to consistently emit a verifier log AND
> > >> a warning. The consistent use of WARN_ONCE should help fuzzers (ex.
> > >> syzkaller) expose any situation where they are actually able to reach
> > >> one of those buggy verifier states.
> > >
> > > No. We cannot do it.
> > > WARN_ONCE is for kernel level issues.
> > > In some configs use panic_on_warn=1 too.
> > > Whereas a verifier bug is contained within a verifier.
> > > It will not bring the kernel down.
> >
> > Agree.
> >
> > > We should remove most of the existing WARN_ONCE instead.
> > > Potentially replace them with pr_info_once().
> >
> > Just a thought, maybe one potential avenue could be to have an equivalent of
> > CONFIG_DEBUG_NET which we make a hard dependency of CONFIG_DEBUG_KERNEL so that
> > /noone/ enables this anywhere in production, and then fuzzers could use it
> > to their advantage. The default case for a DEBUG_BPF_WARN_ONCE would then fall
> > to BUILD_BUG_ON_INVALID() which lets compiler check validity but not generate
> > code.
>
> Good idea.
> but I'm not a fan of the new kconfig.
> I'd rather combine some of them.
> Like make CONFIG_BPF_JIT depend on CONFIG_BPF_SYSCALL.
> That would remove a ton of empty static inline helpers.
>
> For this case doing WARN_ON only when IS_ENABLED(CONFIG_DEBUG_KERNEL)
> should be fine.
>
> I guess we can introduce BPF_WARN_ONCE() and friends family of macros
> and use them everywhere in kernel/bpf/ where the warning is not
> harmful to the kernel ?
Thanks for the reviews! I've sent a v2 that introduces BPF_WARN_ONCE and
uses it in verifier_bug() to emit a warning iff CONFIG_DEBUG_KERNEL is
enabled.
I've refrained from replacing all WARN_ONCE at once as I'm not sure I
have all the context yet to know when BPF_WARN_ONCE is more appropriate.
That said, we should be able to use it for other cases and introduce
sibling macros as we go.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-05-14 11:41 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-08 20:40 [PATCH bpf-next] bpf: Always WARN_ONCE on verifier bugs Paul Chaignon
2025-05-08 22:31 ` Alexei Starovoitov
2025-05-09 8:26 ` Daniel Borkmann
2025-05-09 17:58 ` Alexei Starovoitov
2025-05-14 11:41 ` Paul Chaignon
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.