Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Track delta between "linked" registers.

Compilers can generate the code
r1 = r2
r1 += 0x1
if r2 < 1000 goto ...
use knowledge of r2 range in subsequent r1 operations

So remember constant delta between r2 and r1 and update r1 after 'if' condition.

Unfortunately LLVM still uses this pattern for loops with 'can_loop' construct:
for (i = 0; i < 1000 && can_loop; i++)

The "undo" pass was introduced in LLVM
https://reviews.llvm.org/D121937
to prevent this optimization, but it cannot cover all cases.
Instead of fighting middle end optimizer in BPF backend teach the verifier
about this pattern.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/bpf/20240613013815.953-3-alexei.starovoitov@gmail.com

authored by

Alexei Starovoitov and committed by
Daniel Borkmann
98d7ca37 124e8c2b

+109 -24
+11 -1
include/linux/bpf_verifier.h
··· 73 73 struct bpf_reg_state { 74 74 /* Ordering of fields matters. See states_equal() */ 75 75 enum bpf_reg_type type; 76 - /* Fixed part of pointer offset, pointer types only */ 76 + /* 77 + * Fixed part of pointer offset, pointer types only. 78 + * Or constant delta between "linked" scalars with the same ID. 79 + */ 77 80 s32 off; 78 81 union { 79 82 /* valid when type == PTR_TO_PACKET */ ··· 170 167 * Similarly to dynptrs, we use ID to track "belonging" of a reference 171 168 * to a specific instance of bpf_iter. 172 169 */ 170 + /* 171 + * Upper bit of ID is used to remember relationship between "linked" 172 + * registers. Example: 173 + * r1 = r2; both will have r1->id == r2->id == N 174 + * r1 += 10; r1->id == N | BPF_ADD_CONST and r1->off == 10 175 + */ 176 + #define BPF_ADD_CONST (1U << 31) 173 177 u32 id; 174 178 /* PTR_TO_SOCKET and PTR_TO_TCP_SOCK could be a ptr returned 175 179 * from a pointer-cast helper, bpf_sk_fullsock() and
+3 -1
kernel/bpf/log.c
··· 708 708 verbose(env, "%s", btf_type_name(reg->btf, reg->btf_id)); 709 709 verbose(env, "("); 710 710 if (reg->id) 711 - verbose_a("id=%d", reg->id); 711 + verbose_a("id=%d", reg->id & ~BPF_ADD_CONST); 712 + if (reg->id & BPF_ADD_CONST) 713 + verbose(env, "%+d", reg->off); 712 714 if (reg->ref_obj_id) 713 715 verbose_a("ref_obj_id=%d", reg->ref_obj_id); 714 716 if (type_is_non_owning_ref(reg->type))
+84 -11
kernel/bpf/verifier.c
··· 3991 3991 u32 i; 3992 3992 3993 3993 for (i = 0; i < s->count; ++i) 3994 - if (s->ids[i] == id) 3994 + if (s->ids[i] == (id & ~BPF_ADD_CONST)) 3995 3995 return true; 3996 3996 3997 3997 return false; ··· 4001 4001 { 4002 4002 if (WARN_ON_ONCE(s->count >= ARRAY_SIZE(s->ids))) 4003 4003 return -EFAULT; 4004 - s->ids[s->count++] = id; 4004 + s->ids[s->count++] = id & ~BPF_ADD_CONST; 4005 4005 return 0; 4006 4006 } 4007 4007 ··· 4438 4438 static void assign_scalar_id_before_mov(struct bpf_verifier_env *env, 4439 4439 struct bpf_reg_state *src_reg) 4440 4440 { 4441 - if (src_reg->type == SCALAR_VALUE && !src_reg->id && 4442 - !tnum_is_const(src_reg->var_off)) 4441 + if (src_reg->type != SCALAR_VALUE) 4442 + return; 4443 + 4444 + if (src_reg->id & BPF_ADD_CONST) { 4445 + /* 4446 + * The verifier is processing rX = rY insn and 4447 + * rY->id has special linked register already. 4448 + * Cleared it, since multiple rX += const are not supported. 4449 + */ 4450 + src_reg->id = 0; 4451 + src_reg->off = 0; 4452 + } 4453 + 4454 + if (!src_reg->id && !tnum_is_const(src_reg->var_off)) 4443 4455 /* Ensure that src_reg has a valid ID that will be copied to 4444 4456 * dst_reg and then will be used by find_equal_scalars() to 4445 4457 * propagate min/max range. ··· 14054 14042 struct bpf_func_state *state = vstate->frame[vstate->curframe]; 14055 14043 struct bpf_reg_state *regs = state->regs, *dst_reg, *src_reg; 14056 14044 struct bpf_reg_state *ptr_reg = NULL, off_reg = {0}; 14045 + bool alu32 = (BPF_CLASS(insn->code) != BPF_ALU64); 14057 14046 u8 opcode = BPF_OP(insn->code); 14058 14047 int err; 14059 14048 ··· 14077 14064 14078 14065 if (dst_reg->type != SCALAR_VALUE) 14079 14066 ptr_reg = dst_reg; 14080 - else 14081 - /* Make sure ID is cleared otherwise dst_reg min/max could be 14082 - * incorrectly propagated into other registers by find_equal_scalars() 14083 - */ 14084 - dst_reg->id = 0; 14067 + 14085 14068 if (BPF_SRC(insn->code) == BPF_X) { 14086 14069 src_reg = &regs[insn->src_reg]; 14087 14070 if (src_reg->type != SCALAR_VALUE) { ··· 14141 14132 verbose(env, "verifier internal error: no src_reg\n"); 14142 14133 return -EINVAL; 14143 14134 } 14144 - return adjust_scalar_min_max_vals(env, insn, dst_reg, *src_reg); 14135 + err = adjust_scalar_min_max_vals(env, insn, dst_reg, *src_reg); 14136 + if (err) 14137 + return err; 14138 + /* 14139 + * Compilers can generate the code 14140 + * r1 = r2 14141 + * r1 += 0x1 14142 + * if r2 < 1000 goto ... 14143 + * use r1 in memory access 14144 + * So remember constant delta between r2 and r1 and update r1 after 14145 + * 'if' condition. 14146 + */ 14147 + if (env->bpf_capable && BPF_OP(insn->code) == BPF_ADD && 14148 + dst_reg->id && is_reg_const(src_reg, alu32)) { 14149 + u64 val = reg_const_value(src_reg, alu32); 14150 + 14151 + if ((dst_reg->id & BPF_ADD_CONST) || 14152 + /* prevent overflow in find_equal_scalars() later */ 14153 + val > (u32)S32_MAX) { 14154 + /* 14155 + * If the register already went through rX += val 14156 + * we cannot accumulate another val into rx->off. 14157 + */ 14158 + dst_reg->off = 0; 14159 + dst_reg->id = 0; 14160 + } else { 14161 + dst_reg->id |= BPF_ADD_CONST; 14162 + dst_reg->off = val; 14163 + } 14164 + } else { 14165 + /* 14166 + * Make sure ID is cleared otherwise dst_reg min/max could be 14167 + * incorrectly propagated into other registers by find_equal_scalars() 14168 + */ 14169 + dst_reg->id = 0; 14170 + } 14171 + return 0; 14145 14172 } 14146 14173 14147 14174 /* check validity of 32-bit and 64-bit arithmetic operations */ ··· 15149 15104 static void find_equal_scalars(struct bpf_verifier_state *vstate, 15150 15105 struct bpf_reg_state *known_reg) 15151 15106 { 15107 + struct bpf_reg_state fake_reg; 15152 15108 struct bpf_func_state *state; 15153 15109 struct bpf_reg_state *reg; 15154 15110 15155 15111 bpf_for_each_reg_in_vstate(vstate, state, reg, ({ 15156 - if (reg->type == SCALAR_VALUE && reg->id == known_reg->id) 15112 + if (reg->type != SCALAR_VALUE || reg == known_reg) 15113 + continue; 15114 + if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST)) 15115 + continue; 15116 + if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) || 15117 + reg->off == known_reg->off) { 15157 15118 copy_register_state(reg, known_reg); 15119 + } else { 15120 + s32 saved_off = reg->off; 15121 + 15122 + fake_reg.type = SCALAR_VALUE; 15123 + __mark_reg_known(&fake_reg, (s32)reg->off - (s32)known_reg->off); 15124 + 15125 + /* reg = known_reg; reg += delta */ 15126 + copy_register_state(reg, known_reg); 15127 + /* 15128 + * Must preserve off, id and add_const flag, 15129 + * otherwise another find_equal_scalars() will be incorrect. 15130 + */ 15131 + reg->off = saved_off; 15132 + 15133 + scalar32_min_max_add(reg, &fake_reg); 15134 + scalar_min_max_add(reg, &fake_reg); 15135 + reg->var_off = tnum_add(reg->var_off, fake_reg.var_off); 15136 + } 15158 15137 })); 15159 15138 } 15160 15139 ··· 16807 16738 } 16808 16739 if (!rold->precise && exact == NOT_EXACT) 16809 16740 return true; 16741 + if ((rold->id & BPF_ADD_CONST) != (rcur->id & BPF_ADD_CONST)) 16742 + return false; 16743 + if ((rold->id & BPF_ADD_CONST) && (rold->off != rcur->off)) 16744 + return false; 16810 16745 /* Why check_ids() for scalar registers? 16811 16746 * 16812 16747 * Consider the following BPF code:
+11 -11
tools/testing/selftests/bpf/verifier/precise.c
··· 39 39 .result = VERBOSE_ACCEPT, 40 40 .errstr = 41 41 "mark_precise: frame0: last_idx 26 first_idx 20\ 42 - mark_precise: frame0: regs=r2 stack= before 25\ 43 - mark_precise: frame0: regs=r2 stack= before 24\ 44 - mark_precise: frame0: regs=r2 stack= before 23\ 45 - mark_precise: frame0: regs=r2 stack= before 22\ 46 - mark_precise: frame0: regs=r2 stack= before 20\ 47 - mark_precise: frame0: parent state regs=r2 stack=:\ 42 + mark_precise: frame0: regs=r2,r9 stack= before 25\ 43 + mark_precise: frame0: regs=r2,r9 stack= before 24\ 44 + mark_precise: frame0: regs=r2,r9 stack= before 23\ 45 + mark_precise: frame0: regs=r2,r9 stack= before 22\ 46 + mark_precise: frame0: regs=r2,r9 stack= before 20\ 47 + mark_precise: frame0: parent state regs=r2,r9 stack=:\ 48 48 mark_precise: frame0: last_idx 19 first_idx 10\ 49 49 mark_precise: frame0: regs=r2,r9 stack= before 19\ 50 50 mark_precise: frame0: regs=r9 stack= before 18\ ··· 100 100 .errstr = 101 101 "26: (85) call bpf_probe_read_kernel#113\ 102 102 mark_precise: frame0: last_idx 26 first_idx 22\ 103 - mark_precise: frame0: regs=r2 stack= before 25\ 104 - mark_precise: frame0: regs=r2 stack= before 24\ 105 - mark_precise: frame0: regs=r2 stack= before 23\ 106 - mark_precise: frame0: regs=r2 stack= before 22\ 107 - mark_precise: frame0: parent state regs=r2 stack=:\ 103 + mark_precise: frame0: regs=r2,r9 stack= before 25\ 104 + mark_precise: frame0: regs=r2,r9 stack= before 24\ 105 + mark_precise: frame0: regs=r2,r9 stack= before 23\ 106 + mark_precise: frame0: regs=r2,r9 stack= before 22\ 107 + mark_precise: frame0: parent state regs=r2,r9 stack=:\ 108 108 mark_precise: frame0: last_idx 20 first_idx 20\ 109 109 mark_precise: frame0: regs=r2,r9 stack= before 20\ 110 110 mark_precise: frame0: parent state regs=r2,r9 stack=:\