Skip to content

Commit 1a8a315

Browse files
anakryikoborkmann
authored andcommitted
bpf: Ensure proper register state printing for cond jumps
Verifier emits relevant register state involved in any given instruction next to it after `;` to the right, if possible. Or, worst case, on the separate line repeating instruction index. E.g., a nice and simple case would be: 2: (d5) if r0 s<= 0x0 goto pc+1 ; R0_w=0 But if there is some intervening extra output (e.g., precision backtracking log) involved, we are supposed to see the state after the precision backtrack log: 4: (75) if r0 s>= 0x0 goto pc+1 mark_precise: frame0: last_idx 4 first_idx 0 subseq_idx -1 mark_precise: frame0: regs=r0 stack= before 2: (d5) if r0 s<= 0x0 goto pc+1 mark_precise: frame0: regs=r0 stack= before 1: (b7) r0 = 0 6: R0_w=0 First off, note that in `6: R0_w=0` instruction index corresponds to the next instruction, not to the conditional jump instruction itself, which is wrong and we'll get to that. But besides that, the above is a happy case that does work today. Yet, if it so happens that precision backtracking had to traverse some of the parent states, this `6: R0_w=0` state output would be missing. This is due to a quirk of print_verifier_state() routine, which performs mark_verifier_state_clean(env) at the end. This marks all registers as "non-scratched", which means that subsequent logic to print *relevant* registers (that is, "scratched ones") fails and doesn't see anything relevant to print and skips the output altogether. print_verifier_state() is used both to print instruction context, but also to print an **entire** verifier state indiscriminately, e.g., during precision backtracking (and in a few other situations, like during entering or exiting subprogram). Which means if we have to print entire parent state before getting to printing instruction context state, instruction context is marked as clean and is omitted. Long story short, this is definitely not intentional. So we fix this behavior in this patch by teaching print_verifier_state() to clear scratch state only if it was used to print instruction state, not the parent/callback state. This is determined by print_all option, so if it's not set, we don't clear scratch state. This fixes missing instruction state for these cases. As for the mismatched instruction index, we fix that by making sure we call print_insn_state() early inside check_cond_jmp_op() before we adjusted insn_idx based on jump branch taken logic. And with that we get desired correct information: 9: (16) if w4 == 0x1 goto pc+9 mark_precise: frame0: last_idx 9 first_idx 9 subseq_idx -1 mark_precise: frame0: parent state regs=r4 stack=: R2_w=1944 R4_rw=P1 R10=fp0 mark_precise: frame0: last_idx 8 first_idx 0 subseq_idx 9 mark_precise: frame0: regs=r4 stack= before 8: (66) if w4 s> 0x3 goto pc+5 mark_precise: frame0: regs=r4 stack= before 7: (b7) r4 = 1 9: R4=1 Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: John Fastabend <john.fastabend@gmail.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/bpf/20231011223728.3188086-6-andrii@kernel.org
1 parent 72f8a1d commit 1a8a315

File tree

1 file changed

+6
-1
lines changed

1 file changed

+6
-1
lines changed

kernel/bpf/verifier.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1539,7 +1539,8 @@ static void print_verifier_state(struct bpf_verifier_env *env,
15391539
if (state->in_async_callback_fn)
15401540
verbose(env, " async_cb");
15411541
verbose(env, "\n");
1542-
mark_verifier_state_clean(env);
1542+
if (!print_all)
1543+
mark_verifier_state_clean(env);
15431544
}
15441545

15451546
static inline u32 vlog_alignment(u32 pos)
@@ -14408,6 +14409,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1440814409
!sanitize_speculative_path(env, insn, *insn_idx + 1,
1440914410
*insn_idx))
1441014411
return -EFAULT;
14412+
if (env->log.level & BPF_LOG_LEVEL)
14413+
print_insn_state(env, this_branch->frame[this_branch->curframe]);
1441114414
*insn_idx += insn->off;
1441214415
return 0;
1441314416
} else if (pred == 0) {
@@ -14420,6 +14423,8 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1442014423
*insn_idx + insn->off + 1,
1442114424
*insn_idx))
1442214425
return -EFAULT;
14426+
if (env->log.level & BPF_LOG_LEVEL)
14427+
print_insn_state(env, this_branch->frame[this_branch->curframe]);
1442314428
return 0;
1442414429
}
1442514430

0 commit comments

Comments
 (0)