summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-03-06 18:24:41 -0800
committerAlexei Starovoitov <ast@kernel.org>2026-03-06 21:50:05 -0800
commit325d1ba3cac4a1c0bf981f73f4edbb7411f0e4dc (patch)
tree5b66c9a17e2d54698bcf07c8205f2d4063c1c5f8 /kernel
parent6895e1d7c3171dc29097393749a6f4ebfb316860 (diff)
parent223ffb6a3d0582522455e83ccf7ad2d3a753e039 (diff)
Merge branch 'bpf-fix-precision-backtracking-bug-with-linked-registers'
Eduard Zingerman says: ==================== bpf: Fix precision backtracking bug with linked registers Emil Tsalapatis reported a verifier bug hit by the scx_lavd sched_ext scheduler. The essential part of the verifier log looks as follows: 436: ... // checkpoint hit for 438: (1d) if r7 == r8 goto ... frame 3: propagating r2,r7,r8 frame 2: propagating r6 mark_precise: frame3: last_idx ... mark_precise: frame3: regs=r2,r7,r8 stack= before 436: ... mark_precise: frame3: regs=r2,r7 stack= before 435: ... mark_precise: frame3: regs=r2,r7 stack= before 434: (85) call bpf_trace_vprintk#177 verifier bug: backtracking call unexpected regs 84 The log complains that registers r2 and r7 are tracked as precise while processing the bpf_trace_vprintk() call in precision backtracking. This can't be right, as r2 is reset by the call and there is nothing to backtrack it to. The precision propagation is triggered when a checkpoint is hit at instruction 438, r2 is dead at that instruction. This happens because of the following sequence of events: - Instruction 438 is first reached with registers r2 and r7 having the same id via a path that does not call bpf_trace_vprintk(): - Checkpoint is created at 438. - The jump at 438 is predicted, hence r7 and registers linked to it (r2) are propagated as precise, marking r2 and r7 precise in the checkpoint. - Instruction 438 is reached a second time with r2 undefined and via a path that calls bpf_trace_vprintk(): - Checkpoint is hit. - propagate_precision() picks registers r2 and r7 and propagates precision marks for those up to the helper call. The root cause is the fact that states_equal() and propagate_precision() assume that the precision flag can't be set for a dead register (as computed by compute_live_registers()). However, this is not the case when linked registers are at play. Fix this by accounting for live register flags in collect_linked_regs(). --- ==================== Link: https://patch.msgid.link/20260306-linked-regs-and-propagate-precision-v1-0-18e859be570d@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/verifier.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f960b382fdb3..836ceb128d19 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -17359,17 +17359,24 @@ static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_st
* in verifier state, save R in linked_regs if R->id == id.
* If there are too many Rs sharing same id, reset id for leftover Rs.
*/
-static void collect_linked_regs(struct bpf_verifier_state *vstate, u32 id,
+static void collect_linked_regs(struct bpf_verifier_env *env,
+ struct bpf_verifier_state *vstate,
+ u32 id,
struct linked_regs *linked_regs)
{
+ struct bpf_insn_aux_data *aux = env->insn_aux_data;
struct bpf_func_state *func;
struct bpf_reg_state *reg;
+ u16 live_regs;
int i, j;
id = id & ~BPF_ADD_CONST;
for (i = vstate->curframe; i >= 0; i--) {
+ live_regs = aux[frame_insn_idx(vstate, i)].live_regs_before;
func = vstate->frame[i];
for (j = 0; j < BPF_REG_FP; j++) {
+ if (!(live_regs & BIT(j)))
+ continue;
reg = &func->regs[j];
__collect_linked_regs(linked_regs, reg, id, i, j, true);
}
@@ -17584,9 +17591,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
* if parent state is created.
*/
if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id)
- collect_linked_regs(this_branch, src_reg->id, &linked_regs);
+ collect_linked_regs(env, this_branch, src_reg->id, &linked_regs);
if (dst_reg->type == SCALAR_VALUE && dst_reg->id)
- collect_linked_regs(this_branch, dst_reg->id, &linked_regs);
+ collect_linked_regs(env, this_branch, dst_reg->id, &linked_regs);
if (linked_regs.cnt > 1) {
err = push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs));
if (err)