Skip to content

Commit 54a0555

Browse files
Yonghong Songintel-lab-lkp
authored andcommitted
bpf: Do not include r10 in precision backtracking bookkeeping
Yi Lai reported an issue ([1]) where the following warning appears in kernel dmesg: [ 60.643604] verifier backtracking bug [ 60.643635] WARNING: CPU: 10 PID: 2315 at kernel/bpf/verifier.c:4302 __mark_chain_precision+0x3a6c/0x3e10 [ 60.648428] Modules linked in: bpf_testmod(OE) [ 60.650471] CPU: 10 UID: 0 PID: 2315 Comm: test_progs Tainted: G OE 6.15.0-rc4-gef11287f8289-dirty torvalds#327 PREEMPT(full) [ 60.654385] Tainted: [O]=OOT_MODULE, [E]=UNSIGNED_MODULE [ 60.656682] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.14.0-0-g155821a1990b-prebuilt.qemu.org 04/01/2014 [ 60.660475] RIP: 0010:__mark_chain_precision+0x3a6c/0x3e10 [ 60.662814] Code: 5a 30 84 89 ea e8 c4 d9 01 00 80 3d 3e 7d d8 04 00 0f 85 60 fa ff ff c6 05 31 7d d8 04 01 48 c7 c7 00 58 30 84 e8 c4 06 a5 ff <0f> 0b e9 46 fa ff ff 48 ... [ 60.668720] RSP: 0018:ffff888116cc7298 EFLAGS: 00010246 [ 60.671075] RAX: 54d70e82dfd31900 RBX: ffff888115b65e20 RCX: 0000000000000000 [ 60.673659] RDX: 0000000000000001 RSI: 0000000000000004 RDI: 00000000ffffffff [ 60.676241] RBP: 0000000000000400 R08: ffff8881f6f23bd3 R09: 1ffff1103ede477a [ 60.678787] R10: dffffc0000000000 R11: ffffed103ede477b R12: ffff888115b60ae8 [ 60.681420] R13: 1ffff11022b6cbc4 R14: 00000000fffffff2 R15: 0000000000000001 [ 60.684030] FS: 00007fc2aedd80c0(0000) GS:ffff88826fa8a000(0000) knlGS:0000000000000000 [ 60.686837] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 60.689027] CR2: 000056325369e000 CR3: 000000011088b002 CR4: 0000000000370ef0 [ 60.691623] Call Trace: [ 60.692821] <TASK> [ 60.693960] ? __pfx_verbose+0x10/0x10 [ 60.695656] ? __pfx_disasm_kfunc_name+0x10/0x10 [ 60.697495] check_cond_jmp_op+0x16f7/0x39b0 [ 60.699237] do_check+0x58fa/0xab10 ... Further analysis shows the warning is at line 4302 as below: 4294 /* static subprog call instruction, which 4295 * means that we are exiting current subprog, 4296 * so only r1-r5 could be still requested as 4297 * precise, r0 and r6-r10 or any stack slot in 4298 * the current frame should be zero by now 4299 */ 4300 if (bt_reg_mask(bt) & ~BPF_REGMASK_ARGS) { 4301 verbose(env, "BUG regs %x\n", bt_reg_mask(bt)); 4302 WARN_ONCE(1, "verifier backtracking bug"); 4303 return -EFAULT; 4304 } With the below test (also in the next patch): __used __naked static void __bpf_jmp_r10(void) { asm volatile ( "r2 = 2314885393468386424 ll;" "goto +0;" "if r2 <= r10 goto +3;" "if r1 >= -1835016 goto +0;" "if r2 <= 8 goto +0;" "if r3 <= 0 goto +0;" "exit;" ::: __clobber_all); } SEC("?raw_tp") __naked void bpf_jmp_r10(void) { asm volatile ( "r3 = 0 ll;" "call __bpf_jmp_r10;" "r0 = 0;" "exit;" ::: __clobber_all); } The following is the verifier failure log: 0: (18) r3 = 0x0 ; R3_w=0 2: (85) call pc+2 caller: R10=fp0 callee: frame1: R1=ctx() R3_w=0 R10=fp0 5: frame1: R1=ctx() R3_w=0 R10=fp0 ; asm volatile (" \ @ verifier_precision.c:184 5: (18) r2 = 0x20202000256c6c78 ; frame1: R2_w=0x20202000256c6c78 7: (05) goto pc+0 8: (bd) if r2 <= r10 goto pc+3 ; frame1: R2_w=0x20202000256c6c78 R10=fp0 9: (35) if r1 >= 0xffe3fff8 goto pc+0 ; frame1: R1=ctx() 10: (b5) if r2 <= 0x8 goto pc+0 mark_precise: frame1: last_idx 10 first_idx 0 subseq_idx -1 mark_precise: frame1: regs=r2 stack= before 9: (35) if r1 >= 0xffe3fff8 goto pc+0 mark_precise: frame1: regs=r2 stack= before 8: (bd) if r2 <= r10 goto pc+3 mark_precise: frame1: regs=r2,r10 stack= before 7: (05) goto pc+0 mark_precise: frame1: regs=r2,r10 stack= before 5: (18) r2 = 0x20202000256c6c78 mark_precise: frame1: regs=r10 stack= before 2: (85) call pc+2 BUG regs 400 The main failure reason is due to r10 in precision backtracking bookkeeping. Actually r10 is always precise and there is no need to add it the precision backtracking bookkeeping. One way to fix the issue is to prevent bt_set_reg() if any src/dst reg is r10. Andrii suggested to go with push_insn_history() approach to avoid explicitly checking r10 in backtrack_insn(). This patch added push_insn_history() support for cond_jmp like 'rX <op> rY' operations. In check_cond_jmp_op(), if any of rX or rY is r10, push_insn_history() will not record that register, and later backtrack_insn() will not do bt_set_reg() for those registers. [1] https://lore.kernel.org/bpf/Z%2F8q3xzpU59CIYQE@ly-workstation/ Reported by: Yi Lai <[email protected]> Fixes: 407958a ("bpf: encapsulate precision backtracking bookkeeping") Signed-off-by: Yonghong Song <[email protected]>
1 parent 9325d53 commit 54a0555

File tree

2 files changed

+64
-12
lines changed

2 files changed

+64
-12
lines changed

include/linux/bpf_verifier.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ enum {
357357
INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */
358358

359359
INSN_F_STACK_ACCESS = BIT(9), /* we need 10 bits total */
360+
361+
INSN_F_REG_ACCESS = BIT(4), /* we need 5 bits total */
360362
};
361363

362364
static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES);
@@ -372,6 +374,11 @@ struct bpf_insn_hist_entry {
372374
* jump is backtracked, vector of six 10-bit records
373375
*/
374376
u64 linked_regs;
377+
/* special flag, e.g., whether reg is used for non-load/store insns
378+
* during precision backtracking.
379+
*/
380+
u8 sreg_flag;
381+
u8 dreg_flag;
375382
};
376383

377384
/* Maximum number of register states that can exist at once */

kernel/bpf/verifier.c

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3743,6 +3743,11 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
37433743
return __check_reg_arg(env, state->regs, regno, t);
37443744
}
37453745

3746+
static int insn_reg_with_access_flag(int reg)
3747+
{
3748+
return INSN_F_REG_ACCESS | reg;
3749+
}
3750+
37463751
static int insn_stack_access_flags(int frameno, int spi)
37473752
{
37483753
return INSN_F_STACK_ACCESS | (spi << INSN_F_SPI_SHIFT) | frameno;
@@ -3848,7 +3853,7 @@ static void linked_regs_unpack(u64 val, struct linked_regs *s)
38483853

38493854
/* for any branch, call, exit record the history of jmps in the given state */
38503855
static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
3851-
int insn_flags, u64 linked_regs)
3856+
int insn_flags, u64 linked_regs, u8 sreg_flag, u8 dreg_flag)
38523857
{
38533858
struct bpf_insn_hist_entry *p;
38543859
size_t alloc_size;
@@ -3867,6 +3872,8 @@ static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_s
38673872
"verifier insn history bug: insn_idx %d linked_regs != 0: %#llx\n",
38683873
env->insn_idx, env->cur_hist_ent->linked_regs);
38693874
env->cur_hist_ent->linked_regs = linked_regs;
3875+
env->cur_hist_ent->sreg_flag = sreg_flag;
3876+
env->cur_hist_ent->dreg_flag = dreg_flag;
38703877
return 0;
38713878
}
38723879

@@ -3884,6 +3891,8 @@ static int push_insn_history(struct bpf_verifier_env *env, struct bpf_verifier_s
38843891
p->prev_idx = env->prev_insn_idx;
38853892
p->flags = insn_flags;
38863893
p->linked_regs = linked_regs;
3894+
p->sreg_flag = sreg_flag;
3895+
p->dreg_flag = dreg_flag;
38873896

38883897
cur->insn_hist_end++;
38893898
env->cur_hist_ent = p;
@@ -4406,6 +4415,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
44064415
*/
44074416
return 0;
44084417
} else if (BPF_SRC(insn->code) == BPF_X) {
4418+
bool dreg_precise, sreg_precise;
4419+
44094420
if (!bt_is_reg_set(bt, dreg) && !bt_is_reg_set(bt, sreg))
44104421
return 0;
44114422
/* dreg <cond> sreg
@@ -4414,8 +4425,16 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
44144425
* before it would be equally necessary to
44154426
* propagate it to dreg.
44164427
*/
4417-
bt_set_reg(bt, dreg);
4418-
bt_set_reg(bt, sreg);
4428+
if (!hist)
4429+
return 0;
4430+
dreg_precise = hist->dreg_flag == insn_reg_with_access_flag(dreg);
4431+
sreg_precise = hist->sreg_flag == insn_reg_with_access_flag(sreg);
4432+
if (!dreg_precise && !sreg_precise)
4433+
return 0;
4434+
if (dreg_precise)
4435+
bt_set_reg(bt, dreg);
4436+
if (sreg_precise)
4437+
bt_set_reg(bt, sreg);
44194438
} else if (BPF_SRC(insn->code) == BPF_K) {
44204439
/* dreg <cond> K
44214440
* Only dreg still needs precision before
@@ -5115,7 +5134,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
51155134
}
51165135

51175136
if (insn_flags)
5118-
return push_insn_history(env, env->cur_state, insn_flags, 0);
5137+
return push_insn_history(env, env->cur_state, insn_flags, 0, 0, 0);
51195138
return 0;
51205139
}
51215140

@@ -5422,7 +5441,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
54225441
insn_flags = 0; /* we are not restoring spilled register */
54235442
}
54245443
if (insn_flags)
5425-
return push_insn_history(env, env->cur_state, insn_flags, 0);
5444+
return push_insn_history(env, env->cur_state, insn_flags, 0, 0, 0);
54265445
return 0;
54275446
}
54285447

@@ -16414,6 +16433,27 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
1641416433
}
1641516434
}
1641616435

16436+
static int push_cond_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *state,
16437+
struct bpf_insn *insn, u64 linked_regs)
16438+
{
16439+
int err;
16440+
16441+
if ((BPF_SRC(insn->code) != BPF_X ||
16442+
(insn->src_reg == BPF_REG_FP && insn->dst_reg == BPF_REG_FP)) &&
16443+
!linked_regs)
16444+
return 0;
16445+
16446+
err = push_insn_history(env, state, 0, linked_regs,
16447+
BPF_SRC(insn->code) == BPF_X && insn->src_reg != BPF_REG_FP
16448+
? insn_reg_with_access_flag(insn->src_reg)
16449+
: 0,
16450+
BPF_SRC(insn->code) == BPF_X && insn->dst_reg != BPF_REG_FP
16451+
? insn_reg_with_access_flag(insn->dst_reg)
16452+
: 0);
16453+
16454+
return err;
16455+
}
16456+
1641716457
static int check_cond_jmp_op(struct bpf_verifier_env *env,
1641816458
struct bpf_insn *insn, int *insn_idx)
1641916459
{
@@ -16517,6 +16557,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1651716557
!sanitize_speculative_path(env, insn, *insn_idx + 1,
1651816558
*insn_idx))
1651916559
return -EFAULT;
16560+
err = push_cond_jmp_history(env, this_branch, insn, 0);
16561+
if (err)
16562+
return err;
1652016563
if (env->log.level & BPF_LOG_LEVEL)
1652116564
print_insn_state(env, this_branch, this_branch->curframe);
1652216565
*insn_idx += insn->off;
@@ -16531,6 +16574,9 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1653116574
*insn_idx + insn->off + 1,
1653216575
*insn_idx))
1653316576
return -EFAULT;
16577+
err = push_cond_jmp_history(env, this_branch, insn, 0);
16578+
if (err)
16579+
return err;
1653416580
if (env->log.level & BPF_LOG_LEVEL)
1653516581
print_insn_state(env, this_branch, this_branch->curframe);
1653616582
return 0;
@@ -16545,11 +16591,10 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
1654516591
collect_linked_regs(this_branch, src_reg->id, &linked_regs);
1654616592
if (dst_reg->type == SCALAR_VALUE && dst_reg->id)
1654716593
collect_linked_regs(this_branch, dst_reg->id, &linked_regs);
16548-
if (linked_regs.cnt > 1) {
16549-
err = push_insn_history(env, this_branch, 0, linked_regs_pack(&linked_regs));
16550-
if (err)
16551-
return err;
16552-
}
16594+
err = push_cond_jmp_history(env, this_branch, insn,
16595+
linked_regs.cnt > 1 ? linked_regs_pack(&linked_regs) : 0);
16596+
if (err)
16597+
return err;
1655316598

1655416599
other_branch = push_stack(env, *insn_idx + insn->off + 1, *insn_idx,
1655516600
false);
@@ -19243,7 +19288,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
1924319288
* the current state.
1924419289
*/
1924519290
if (is_jmp_point(env, env->insn_idx))
19246-
err = err ? : push_insn_history(env, cur, 0, 0);
19291+
err = err ? : push_insn_history(env, cur, 0, 0, 0, 0);
1924719292
err = err ? : propagate_precision(env, &sl->state);
1924819293
if (err)
1924919294
return err;
@@ -19494,7 +19539,7 @@ static int do_check(struct bpf_verifier_env *env)
1949419539
}
1949519540

1949619541
if (is_jmp_point(env, env->insn_idx)) {
19497-
err = push_insn_history(env, state, 0, 0);
19542+
err = push_insn_history(env, state, 0, 0, 0, 0);
1949819543
if (err)
1949919544
return err;
1950019545
}

0 commit comments

Comments
 (0)