* [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs
@ 2025-05-14 11:35 Paul Chaignon
2025-05-14 16:28 ` Andrii Nakryiko
0 siblings, 1 reply; 4+ messages in thread
From: Paul Chaignon @ 2025-05-14 11:35 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 when CONFIG_DEBUG_KERNEL is enabled. 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>
---
Changes in v2:
- Introduce a new BPF_WARN_ONCE macro, with WARN_ONCE conditioned on
CONFIG_DEBUG_KERNEL, as per reviews.
- Use the new helper function for verifier bugs missed in v1,
particularly around backtracking.
include/linux/bpf.h | 6 ++
include/linux/bpf_verifier.h | 6 ++
kernel/bpf/btf.c | 4 +-
kernel/bpf/verifier.c | 119 +++++++++++++++++------------------
4 files changed, 70 insertions(+), 65 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 83c56f40842b..5b25d278409b 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -346,6 +346,12 @@ static inline const char *btf_field_type_name(enum btf_field_type type)
}
}
+#if IS_ENABLED(CONFIG_DEBUG_KERNEL)
+#define BPF_WARN_ONCE(cond, format...) WARN_ONCE(cond, format)
+#else
+#define BPF_WARN_ONCE(cond, format...) BUILD_BUG_ON_INVALID(cond)
+#endif
+
static inline u32 btf_field_type_size(enum btf_field_type type)
{
switch (type) {
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 9734544b6957..6f809ad3d3dd 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -838,6 +838,12 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
u32 insn_off,
const char *prefix_fmt, ...);
+#define verifier_bug(env, fmt, args...) \
+ do { \
+ BPF_WARN_ONCE(1, "verifier bug: " fmt, ##args); \
+ bpf_log(&env->log, "verifier bug: " fmt, ##args); \
+ } while (0)
+
static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env)
{
struct bpf_verifier_state *cur = env->cur_state;
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 6b21ca67070c..743b45d80269 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7659,7 +7659,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
return 0;
if (!prog->aux->func_info) {
- bpf_log(log, "Verifier bug\n");
+ verifier_bug(env, "func_info undefined\n");
return -EFAULT;
}
@@ -7683,7 +7683,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
tname = btf_name_by_offset(btf, fn_t->name_off);
if (prog->aux->func_info_aux[subprog].unreliable) {
- bpf_log(log, "Verifier bug in function %s()\n", tname);
+ verifier_bug(env, "unreliable BTF for function %s()\n", tname);
return -EFAULT;
}
if (prog_type == BPF_PROG_TYPE_EXT)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 28f5a7899bd6..49eea17c7b17 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1924,8 +1924,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 +3459,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
@@ -3857,14 +3856,14 @@ static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_s
/* atomic instructions push insn_flags twice, for READ and
* WRITE sides, but they should agree on stack slot
*/
- WARN_ONCE((env->cur_hist_ent->flags & insn_flags) &&
- (env->cur_hist_ent->flags & insn_flags) != insn_flags,
- "verifier insn history bug: insn_idx %d cur flags %x new flags %x\n",
- env->insn_idx, env->cur_hist_ent->flags, insn_flags);
+ if (unlikely((env->cur_hist_ent->flags & insn_flags) &&
+ (env->cur_hist_ent->flags & insn_flags) != insn_flags))
+ verifier_bug(env, "insn history: insn_idx %d cur flags %x new flags %x\n",
+ env->insn_idx, env->cur_hist_ent->flags, insn_flags);
env->cur_hist_ent->flags |= insn_flags;
- WARN_ONCE(env->cur_hist_ent->linked_regs != 0,
- "verifier insn history bug: insn_idx %d linked_regs != 0: %#llx\n",
- env->insn_idx, env->cur_hist_ent->linked_regs);
+ if (unlikely(env->cur_hist_ent->linked_regs != 0))
+ verifier_bug(env, "insn history: insn_idx %d linked_regs != 0: %#llx\n",
+ env->insn_idx, env->cur_hist_ent->linked_regs);
env->cur_hist_ent->linked_regs = linked_regs;
return 0;
}
@@ -3987,8 +3986,7 @@ static inline u32 bt_empty(struct backtrack_state *bt)
static inline int bt_subprog_enter(struct backtrack_state *bt)
{
if (bt->frame == MAX_CALL_FRAMES - 1) {
- verbose(bt->env, "BUG subprog enter from frame %d\n", bt->frame);
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(bt->env, "subprog enter from frame %d\n", bt->frame);
return -EFAULT;
}
bt->frame++;
@@ -3998,8 +3996,7 @@ static inline int bt_subprog_enter(struct backtrack_state *bt)
static inline int bt_subprog_exit(struct backtrack_state *bt)
{
if (bt->frame == 0) {
- verbose(bt->env, "BUG subprog exit from frame 0\n");
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(bt->env, "subprog exit from frame 0\n");
return -EFAULT;
}
bt->frame--;
@@ -4277,14 +4274,15 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* should be literally next instruction in
* caller program
*/
- WARN_ONCE(idx + 1 != subseq_idx, "verifier backtracking bug");
+ if (unlikely(idx + 1 != subseq_idx))
+ verifier_bug(env, "extra insn from subprog");
/* r1-r5 are invalidated after subprog call,
* so for global func call it shouldn't be set
* anymore
*/
if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
- verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "scratch reg set: regs %x\n",
+ bt_reg_mask(bt));
return -EFAULT;
}
/* global subprog always sets R0 */
@@ -4298,16 +4296,16 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* the current frame should be zero by now
*/
if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) {
- verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "unexpected precise regs %x\n",
+ bt_reg_mask(bt));
return -EFAULT;
}
/* we are now tracking register spills correctly,
* so any instance of leftover slots is a bug
*/
if (bt_stack_mask(bt) != 0) {
- verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug (subprog leftover stack slots)");
+ verifier_bug(env, "subprog leftover stack slots %llx\n",
+ bt_stack_mask(bt));
return -EFAULT;
}
/* propagate r1-r5 to the caller */
@@ -4330,13 +4328,13 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* not actually arguments passed directly to callback subprogs
*/
if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) {
- verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "unexpected precise regs %x\n",
+ bt_reg_mask(bt));
return -EFAULT;
}
if (bt_stack_mask(bt) != 0) {
- verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug (callback leftover stack slots)");
+ verifier_bug(env, "callback leftover stack slots %llx\n",
+ bt_stack_mask(bt));
return -EFAULT;
}
/* clear r1-r5 in callback subprog's mask */
@@ -4358,8 +4356,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
/* if backtracing was looking for registers R1-R5
* they should have been found already.
*/
- verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "regs not found %x\n", bt_reg_mask(bt));
return -EFAULT;
}
} else if (opcode == BPF_EXIT) {
@@ -4377,8 +4374,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
for (i = BPF_REG_1; i <= BPF_REG_5; i++)
bt_clear_reg(bt, i);
if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
- verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "regs not found %x\n", bt_reg_mask(bt));
return -EFAULT;
}
@@ -4719,9 +4715,8 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
return 0;
}
- verbose(env, "BUG backtracking func entry subprog %d reg_mask %x stack_mask %llx\n",
- st->frame[0]->subprogno, bt_reg_mask(bt), bt_stack_mask(bt));
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "backtracking func entry subprog %d reg_mask %x stack_mask %llx\n",
+ st->frame[0]->subprogno, bt_reg_mask(bt), bt_stack_mask(bt));
return -EFAULT;
}
@@ -4757,8 +4752,7 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
* It means the backtracking missed the spot where
* particular register was initialized with a constant.
*/
- verbose(env, "BUG backtracking idx %d\n", i);
- WARN_ONCE(1, "verifier backtracking bug");
+ verifier_bug(env, "backtracking idx %d\n", i);
return -EFAULT;
}
}
@@ -4784,9 +4778,8 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno)
bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr));
for_each_set_bit(i, mask, 64) {
if (i >= func->allocated_stack / BPF_REG_SIZE) {
- verbose(env, "BUG backtracking (stack slot %d, total slots %d)\n",
- i, func->allocated_stack / BPF_REG_SIZE);
- WARN_ONCE(1, "verifier backtracking bug (stack slot out of bounds)");
+ verifier_bug(env, "stack slot %d, total slots %d\n",
+ i, func->allocated_stack / BPF_REG_SIZE);
return -EFAULT;
}
@@ -6562,13 +6555,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 +6669,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 +7977,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 +8406,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 +10278,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 +10393,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 +10457,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 +10516,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 +11117,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 +19682,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 +20743,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 +21064,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 +22426,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] 4+ messages in thread* Re: [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs
2025-05-14 11:35 [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs Paul Chaignon
@ 2025-05-14 16:28 ` Andrii Nakryiko
2025-05-15 19:58 ` Paul Chaignon
0 siblings, 1 reply; 4+ messages in thread
From: Andrii Nakryiko @ 2025-05-14 16:28 UTC (permalink / raw)
To: Paul Chaignon; +Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
On Wed, May 14, 2025 at 4:35 AM 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 when CONFIG_DEBUG_KERNEL is enabled. 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>
> ---
> Changes in v2:
> - Introduce a new BPF_WARN_ONCE macro, with WARN_ONCE conditioned on
> CONFIG_DEBUG_KERNEL, as per reviews.
> - Use the new helper function for verifier bugs missed in v1,
> particularly around backtracking.
>
[...]
> @@ -4277,14 +4274,15 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
> * should be literally next instruction in
> * caller program
> */
> - WARN_ONCE(idx + 1 != subseq_idx, "verifier backtracking bug");
> + if (unlikely(idx + 1 != subseq_idx))
> + verifier_bug(env, "extra insn from subprog");
maybe let's add verifier_buf_if(cond, env, fmt, args...) that would do
if (unlikely(...)) inside? it can also return the result of cond if we
want to do something like
if (verifier_bug_if(<condition>, env, "we are doomed"))
return -EFAULT;
> /* r1-r5 are invalidated after subprog call,
> * so for global func call it shouldn't be set
> * anymore
> */
> if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
> - verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
> - WARN_ONCE(1, "verifier backtracking bug");
> + verifier_bug(env, "scratch reg set: regs %x\n",
> + bt_reg_mask(bt));
> return -EFAULT;
but please don't go overboard with verifier_buf_if() for cases like
this, I think this should use plain verifier_bug() as you did, even if
it *can* be expressed with verifier_buf_if() check
> }
> /* global subprog always sets R0 */
> @@ -4298,16 +4296,16 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
> * the current frame should be zero by now
> */
> if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) {
> - verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
> - WARN_ONCE(1, "verifier backtracking bug");
> + verifier_bug(env, "unexpected precise regs %x\n",
> + bt_reg_mask(bt));
> return -EFAULT;
> }
> /* we are now tracking register spills correctly,
> * so any instance of leftover slots is a bug
> */
> if (bt_stack_mask(bt) != 0) {
> - verbose(env, "BUG stack slots %llx\n", bt_stack_mask(bt));
> - WARN_ONCE(1, "verifier backtracking bug (subprog leftover stack slots)");
> + verifier_bug(env, "subprog leftover stack slots %llx\n",
> + bt_stack_mask(bt));
> return -EFAULT;
> }
> /* propagate r1-r5 to the caller */
[...]
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs
2025-05-14 16:28 ` Andrii Nakryiko
@ 2025-05-15 19:58 ` Paul Chaignon
2025-05-15 21:10 ` Andrii Nakryiko
0 siblings, 1 reply; 4+ messages in thread
From: Paul Chaignon @ 2025-05-15 19:58 UTC (permalink / raw)
To: Andrii Nakryiko; +Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
On Wed, May 14, 2025 at 09:28:11AM -0700, Andrii Nakryiko wrote:
> On Wed, May 14, 2025 at 4:35 AM 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 when CONFIG_DEBUG_KERNEL is enabled. 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>
> > ---
> > Changes in v2:
> > - Introduce a new BPF_WARN_ONCE macro, with WARN_ONCE conditioned on
> > CONFIG_DEBUG_KERNEL, as per reviews.
> > - Use the new helper function for verifier bugs missed in v1,
> > particularly around backtracking.
> >
[...]
> > /* r1-r5 are invalidated after subprog call,
> > * so for global func call it shouldn't be set
> > * anymore
> > */
> > if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
> > - verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
> > - WARN_ONCE(1, "verifier backtracking bug");
> > + verifier_bug(env, "scratch reg set: regs %x\n",
> > + bt_reg_mask(bt));
> > return -EFAULT;
>
>
> but please don't go overboard with verifier_buf_if() for cases like
> this, I think this should use plain verifier_bug() as you did, even if
> it *can* be expressed with verifier_buf_if() check
Where would you set the bar on these cases? Is it mostly a matter of
readability?
I'm asking because, with Alexei's suggestion to stringify the condition
in verifier_bug_if() (cf. v3), we would gain from using verifier_bug_if
more often.
[...]
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs
2025-05-15 19:58 ` Paul Chaignon
@ 2025-05-15 21:10 ` Andrii Nakryiko
0 siblings, 0 replies; 4+ messages in thread
From: Andrii Nakryiko @ 2025-05-15 21:10 UTC (permalink / raw)
To: Paul Chaignon; +Cc: bpf, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
On Thu, May 15, 2025 at 12:58 PM Paul Chaignon <paul.chaignon@gmail.com> wrote:
>
> On Wed, May 14, 2025 at 09:28:11AM -0700, Andrii Nakryiko wrote:
> > On Wed, May 14, 2025 at 4:35 AM 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 when CONFIG_DEBUG_KERNEL is enabled. 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>
> > > ---
> > > Changes in v2:
> > > - Introduce a new BPF_WARN_ONCE macro, with WARN_ONCE conditioned on
> > > CONFIG_DEBUG_KERNEL, as per reviews.
> > > - Use the new helper function for verifier bugs missed in v1,
> > > particularly around backtracking.
> > >
>
> [...]
>
> > > /* r1-r5 are invalidated after subprog call,
> > > * so for global func call it shouldn't be set
> > > * anymore
> > > */
> > > if (bt_reg_mask(bt) & BPF_REGMASK_ARGS) {
> > > - verbose(env, "BUG regs %x\n", bt_reg_mask(bt));
> > > - WARN_ONCE(1, "verifier backtracking bug");
> > > + verifier_bug(env, "scratch reg set: regs %x\n",
> > > + bt_reg_mask(bt));
> > > return -EFAULT;
> >
> >
> > but please don't go overboard with verifier_buf_if() for cases like
> > this, I think this should use plain verifier_bug() as you did, even if
> > it *can* be expressed with verifier_buf_if() check
>
> Where would you set the bar on these cases? Is it mostly a matter of
> readability?
yes, it's all subjective, there is no algorithm and no clear cut
boundary on what's readable or not, so it's no big deal
>
> I'm asking because, with Alexei's suggestion to stringify the condition
> in verifier_bug_if() (cf. v3), we would gain from using verifier_bug_if
> more often.
>
> [...]
>
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-05-15 21:11 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-14 11:35 [PATCH bpf-next v2] bpf: WARN_ONCE on verifier bugs Paul Chaignon
2025-05-14 16:28 ` Andrii Nakryiko
2025-05-15 19:58 ` Paul Chaignon
2025-05-15 21:10 ` Andrii Nakryiko
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.