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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bpf-track-find_equal_scalars-history-on-per-instruction-level'

Eduard Zingerman says:

====================
bpf: track find_equal_scalars history on per-instruction level

This is a fix for precision tracking bug reported in [0].
It supersedes my previous attempt to fix similar issue in commit [1].
Here is a minimized test case from [0]:

0: call bpf_get_prandom_u32;
1: r7 = r0;
2: r8 = r0;
3: call bpf_get_prandom_u32;
4: if r0 > 1 goto +0;
/* --- checkpoint #1: r7.id=1, r8.id=1 --- */
5: if r8 >= r0 goto 9f;
6: r8 += r8;
/* --- checkpoint #2: r7.id=1, r8.id=0 --- */
7: if r7 == 0 goto 9f;
8: r0 /= 0;
/* --- checkpoint #3 --- */
9: r0 = 42;
10: exit;

W/o this fix verifier incorrectly assumes that instruction at label
(8) is unreachable. The issue is caused by failure to infer
precision mark for r0 at checkpoint #1:
- first verification path is:
- (0-4): r0 range [0,1];
- (5): r8 range [0,0], propagated to r7;
- (6): r8.id is reset;
- (7): jump is predicted to happen;
- (9-10): safe exit.
- when jump at (7) is predicted mark_chain_precision() for r7 is
called and backtrack_insn() proceeds as follows:
- at (7) r7 is marked as precise;
- at (5) r8 is not currently tracked and thus r0 is not marked;
- at (4-5) boundary logic from [1] is triggered and r7,r8 are marked
as precise;
- => r0 precision mark is missed.
- when second branch of (4) is considered, verifier prunes the state
because r0 is not marked as precise in the visited state.

Basically, backtracking logic fails to notice that at (5)
range information is gained for both r7 and r8, and thus both
r8 and r0 have to be marked as precise.
This happens because [1] can only account for such range
transfers at parent/child state boundaries.

The solution suggested by Andrii Nakryiko in [0] is to use jump
history to remember which registers gained range as a result of
find_equal_scalars() [renamed to sync_linked_regs()] and use
this information in backtrack_insn().
Which is what this patch-set does.

The patch-set uses u64 value as a vector of 10-bit values that
identify registers gaining range in find_equal_scalars().
This amounts to maximum of 6 possible values.
To check if such capacity is sufficient I've instrumented kernel
to track a histogram for maximal amount of registers that gain range
in find_equal_scalars per program verification [2].
Measurements done for verifier selftests and Cilium bpf object files
from [3] show that number of such registers is *always* <= 4 and
in 98% of cases it is <= 2.

When tested on a subset of selftests identified by
selftests/bpf/veristat.cfg and Cilium bpf object files from [3]
this patch-set has minimal verification performance impact:

File Program Insns (DIFF) States (DIFF)
------------------------ ------------------------ -------------- -------------
bpf_host.o tail_handle_nat_fwd_ipv4 -75 (-0.61%) -3 (-0.39%)
pyperf600_nounroll.bpf.o on_event +1673 (+0.33%) +3 (+0.01%)

[0] https://lore.kernel.org/bpf/CAEf4BzZ0xidVCqB47XnkXcNhkPWF6_nTV7yt+_Lf0kcFEut2Mg@mail.gmail.com/
[1] commit 904e6ddf4133 ("bpf: Use scalar ids in mark_chain_precision()")
[2] https://github.com/eddyz87/bpf/tree/find-equal-scalars-in-jump-history-with-stats
[3] https://github.com/anakryiko/cilium

Changes:
- v2 -> v3:
A number of stylistic changes suggested by Andrii:
- renamings:
- struct reg_or_spill -> linked_reg;
- find_equal_scalars() -> collect_linked_regs;
- copy_known_reg() -> sync_linked_regs;
- collect_linked_regs() now returns linked regs set of
size 2 or larger;
- dropped usage of bit fields in struct linked_reg;
- added a patch changing references to find_equal_scalars() in
selftests comments.
- v1 -> v2:
- patch "bpf: replace env->cur_hist_ent with a getter function" is
dropped (Andrii);
- added structure linked_regs and helper functions to [de]serialize
u64 value as such structure (Andrii);
- bt_set_equal_scalars() renamed to bt_sync_linked_regs(), moved to
start and end of backtrack_insn() in order to untie linked
register logic from conditional jumps backtracking.
Andrii requested a more radical change of moving linked registers
processing to bt_set_xxx() functions, I did an experiment in this
direction:
https://github.com/eddyz87/bpf/tree/find-equal-scalars-in-jump-history--linked-regs-in-bt-set-reg
the end result of the experiment seems much uglier than version
presented in v2.

Revisions:
- v1: https://lore.kernel.org/bpf/20240222005005.31784-1-eddyz87@gmail.com/
- v2: https://lore.kernel.org/bpf/20240705205851.2635794-1-eddyz87@gmail.com/
====================

Link: https://lore.kernel.org/r/20240718202357.1746514-1-eddyz87@gmail.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+471 -275
+4
include/linux/bpf_verifier.h
··· 371 371 u32 prev_idx : 22; 372 372 /* special flags, e.g., whether insn is doing register stack spill/load */ 373 373 u32 flags : 10; 374 + /* additional registers that need precision tracking when this 375 + * jump is backtracked, vector of six 10-bit records 376 + */ 377 + u64 linked_regs; 374 378 }; 375 379 376 380 /* Maximum number of register states that can exist at once */
+224 -136
kernel/bpf/verifier.c
··· 3335 3335 return env->insn_aux_data[insn_idx].jmp_point; 3336 3336 } 3337 3337 3338 + #define LR_FRAMENO_BITS 3 3339 + #define LR_SPI_BITS 6 3340 + #define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1) 3341 + #define LR_SIZE_BITS 4 3342 + #define LR_FRAMENO_MASK ((1ull << LR_FRAMENO_BITS) - 1) 3343 + #define LR_SPI_MASK ((1ull << LR_SPI_BITS) - 1) 3344 + #define LR_SIZE_MASK ((1ull << LR_SIZE_BITS) - 1) 3345 + #define LR_SPI_OFF LR_FRAMENO_BITS 3346 + #define LR_IS_REG_OFF (LR_SPI_BITS + LR_FRAMENO_BITS) 3347 + #define LINKED_REGS_MAX 6 3348 + 3349 + struct linked_reg { 3350 + u8 frameno; 3351 + union { 3352 + u8 spi; 3353 + u8 regno; 3354 + }; 3355 + bool is_reg; 3356 + }; 3357 + 3358 + struct linked_regs { 3359 + int cnt; 3360 + struct linked_reg entries[LINKED_REGS_MAX]; 3361 + }; 3362 + 3363 + static struct linked_reg *linked_regs_push(struct linked_regs *s) 3364 + { 3365 + if (s->cnt < LINKED_REGS_MAX) 3366 + return &s->entries[s->cnt++]; 3367 + 3368 + return NULL; 3369 + } 3370 + 3371 + /* Use u64 as a vector of 6 10-bit values, use first 4-bits to track 3372 + * number of elements currently in stack. 3373 + * Pack one history entry for linked registers as 10 bits in the following format: 3374 + * - 3-bits frameno 3375 + * - 6-bits spi_or_reg 3376 + * - 1-bit is_reg 3377 + */ 3378 + static u64 linked_regs_pack(struct linked_regs *s) 3379 + { 3380 + u64 val = 0; 3381 + int i; 3382 + 3383 + for (i = 0; i < s->cnt; ++i) { 3384 + struct linked_reg *e = &s->entries[i]; 3385 + u64 tmp = 0; 3386 + 3387 + tmp |= e->frameno; 3388 + tmp |= e->spi << LR_SPI_OFF; 3389 + tmp |= (e->is_reg ? 1 : 0) << LR_IS_REG_OFF; 3390 + 3391 + val <<= LR_ENTRY_BITS; 3392 + val |= tmp; 3393 + } 3394 + val <<= LR_SIZE_BITS; 3395 + val |= s->cnt; 3396 + return val; 3397 + } 3398 + 3399 + static void linked_regs_unpack(u64 val, struct linked_regs *s) 3400 + { 3401 + int i; 3402 + 3403 + s->cnt = val & LR_SIZE_MASK; 3404 + val >>= LR_SIZE_BITS; 3405 + 3406 + for (i = 0; i < s->cnt; ++i) { 3407 + struct linked_reg *e = &s->entries[i]; 3408 + 3409 + e->frameno = val & LR_FRAMENO_MASK; 3410 + e->spi = (val >> LR_SPI_OFF) & LR_SPI_MASK; 3411 + e->is_reg = (val >> LR_IS_REG_OFF) & 0x1; 3412 + val >>= LR_ENTRY_BITS; 3413 + } 3414 + } 3415 + 3338 3416 /* for any branch, call, exit record the history of jmps in the given state */ 3339 3417 static int push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur, 3340 - int insn_flags) 3418 + int insn_flags, u64 linked_regs) 3341 3419 { 3342 3420 u32 cnt = cur->jmp_history_cnt; 3343 3421 struct bpf_jmp_history_entry *p; ··· 3431 3353 "verifier insn history bug: insn_idx %d cur flags %x new flags %x\n", 3432 3354 env->insn_idx, env->cur_hist_ent->flags, insn_flags); 3433 3355 env->cur_hist_ent->flags |= insn_flags; 3356 + WARN_ONCE(env->cur_hist_ent->linked_regs != 0, 3357 + "verifier insn history bug: insn_idx %d linked_regs != 0: %#llx\n", 3358 + env->insn_idx, env->cur_hist_ent->linked_regs); 3359 + env->cur_hist_ent->linked_regs = linked_regs; 3434 3360 return 0; 3435 3361 } 3436 3362 ··· 3449 3367 p->idx = env->insn_idx; 3450 3368 p->prev_idx = env->prev_insn_idx; 3451 3369 p->flags = insn_flags; 3370 + p->linked_regs = linked_regs; 3452 3371 cur->jmp_history_cnt = cnt; 3453 3372 env->cur_hist_ent = p; 3454 3373 ··· 3615 3532 return bt->reg_masks[bt->frame] & (1 << reg); 3616 3533 } 3617 3534 3535 + static inline bool bt_is_frame_reg_set(struct backtrack_state *bt, u32 frame, u32 reg) 3536 + { 3537 + return bt->reg_masks[frame] & (1 << reg); 3538 + } 3539 + 3618 3540 static inline bool bt_is_frame_slot_set(struct backtrack_state *bt, u32 frame, u32 slot) 3619 3541 { 3620 3542 return bt->stack_masks[frame] & (1ull << slot); ··· 3664 3576 } 3665 3577 } 3666 3578 3579 + /* If any register R in hist->linked_regs is marked as precise in bt, 3580 + * do bt_set_frame_{reg,slot}(bt, R) for all registers in hist->linked_regs. 3581 + */ 3582 + static void bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_jmp_history_entry *hist) 3583 + { 3584 + struct linked_regs linked_regs; 3585 + bool some_precise = false; 3586 + int i; 3587 + 3588 + if (!hist || hist->linked_regs == 0) 3589 + return; 3590 + 3591 + linked_regs_unpack(hist->linked_regs, &linked_regs); 3592 + for (i = 0; i < linked_regs.cnt; ++i) { 3593 + struct linked_reg *e = &linked_regs.entries[i]; 3594 + 3595 + if ((e->is_reg && bt_is_frame_reg_set(bt, e->frameno, e->regno)) || 3596 + (!e->is_reg && bt_is_frame_slot_set(bt, e->frameno, e->spi))) { 3597 + some_precise = true; 3598 + break; 3599 + } 3600 + } 3601 + 3602 + if (!some_precise) 3603 + return; 3604 + 3605 + for (i = 0; i < linked_regs.cnt; ++i) { 3606 + struct linked_reg *e = &linked_regs.entries[i]; 3607 + 3608 + if (e->is_reg) 3609 + bt_set_frame_reg(bt, e->frameno, e->regno); 3610 + else 3611 + bt_set_frame_slot(bt, e->frameno, e->spi); 3612 + } 3613 + } 3614 + 3667 3615 static bool calls_callback(struct bpf_verifier_env *env, int insn_idx); 3668 3616 3669 3617 /* For given verifier state backtrack_insn() is called from the last insn to ··· 3738 3614 verbose(env, "%d: ", idx); 3739 3615 print_bpf_insn(&cbs, insn, env->allow_ptr_leaks); 3740 3616 } 3617 + 3618 + /* If there is a history record that some registers gained range at this insn, 3619 + * propagate precision marks to those registers, so that bt_is_reg_set() 3620 + * accounts for these registers. 3621 + */ 3622 + bt_sync_linked_regs(bt, hist); 3741 3623 3742 3624 if (class == BPF_ALU || class == BPF_ALU64) { 3743 3625 if (!bt_is_reg_set(bt, dreg)) ··· 3974 3844 */ 3975 3845 bt_set_reg(bt, dreg); 3976 3846 bt_set_reg(bt, sreg); 3977 - /* else dreg <cond> K 3847 + } else if (BPF_SRC(insn->code) == BPF_K) { 3848 + /* dreg <cond> K 3978 3849 * Only dreg still needs precision before 3979 3850 * this insn, so for the K-based conditional 3980 3851 * there is nothing new to be marked. ··· 3993 3862 /* to be analyzed */ 3994 3863 return -ENOTSUPP; 3995 3864 } 3865 + /* Propagate precision marks to linked registers, to account for 3866 + * registers marked as precise in this function. 3867 + */ 3868 + bt_sync_linked_regs(bt, hist); 3996 3869 return 0; 3997 3870 } 3998 3871 ··· 4122 3987 reg->precise = false; 4123 3988 } 4124 3989 } 4125 - } 4126 - 4127 - static bool idset_contains(struct bpf_idset *s, u32 id) 4128 - { 4129 - u32 i; 4130 - 4131 - for (i = 0; i < s->count; ++i) 4132 - if (s->ids[i] == (id & ~BPF_ADD_CONST)) 4133 - return true; 4134 - 4135 - return false; 4136 - } 4137 - 4138 - static int idset_push(struct bpf_idset *s, u32 id) 4139 - { 4140 - if (WARN_ON_ONCE(s->count >= ARRAY_SIZE(s->ids))) 4141 - return -EFAULT; 4142 - s->ids[s->count++] = id & ~BPF_ADD_CONST; 4143 - return 0; 4144 - } 4145 - 4146 - static void idset_reset(struct bpf_idset *s) 4147 - { 4148 - s->count = 0; 4149 - } 4150 - 4151 - /* Collect a set of IDs for all registers currently marked as precise in env->bt. 4152 - * Mark all registers with these IDs as precise. 4153 - */ 4154 - static int mark_precise_scalar_ids(struct bpf_verifier_env *env, struct bpf_verifier_state *st) 4155 - { 4156 - struct bpf_idset *precise_ids = &env->idset_scratch; 4157 - struct backtrack_state *bt = &env->bt; 4158 - struct bpf_func_state *func; 4159 - struct bpf_reg_state *reg; 4160 - DECLARE_BITMAP(mask, 64); 4161 - int i, fr; 4162 - 4163 - idset_reset(precise_ids); 4164 - 4165 - for (fr = bt->frame; fr >= 0; fr--) { 4166 - func = st->frame[fr]; 4167 - 4168 - bitmap_from_u64(mask, bt_frame_reg_mask(bt, fr)); 4169 - for_each_set_bit(i, mask, 32) { 4170 - reg = &func->regs[i]; 4171 - if (!reg->id || reg->type != SCALAR_VALUE) 4172 - continue; 4173 - if (idset_push(precise_ids, reg->id)) 4174 - return -EFAULT; 4175 - } 4176 - 4177 - bitmap_from_u64(mask, bt_frame_stack_mask(bt, fr)); 4178 - for_each_set_bit(i, mask, 64) { 4179 - if (i >= func->allocated_stack / BPF_REG_SIZE) 4180 - break; 4181 - if (!is_spilled_scalar_reg(&func->stack[i])) 4182 - continue; 4183 - reg = &func->stack[i].spilled_ptr; 4184 - if (!reg->id) 4185 - continue; 4186 - if (idset_push(precise_ids, reg->id)) 4187 - return -EFAULT; 4188 - } 4189 - } 4190 - 4191 - for (fr = 0; fr <= st->curframe; ++fr) { 4192 - func = st->frame[fr]; 4193 - 4194 - for (i = BPF_REG_0; i < BPF_REG_10; ++i) { 4195 - reg = &func->regs[i]; 4196 - if (!reg->id) 4197 - continue; 4198 - if (!idset_contains(precise_ids, reg->id)) 4199 - continue; 4200 - bt_set_frame_reg(bt, fr, i); 4201 - } 4202 - for (i = 0; i < func->allocated_stack / BPF_REG_SIZE; ++i) { 4203 - if (!is_spilled_scalar_reg(&func->stack[i])) 4204 - continue; 4205 - reg = &func->stack[i].spilled_ptr; 4206 - if (!reg->id) 4207 - continue; 4208 - if (!idset_contains(precise_ids, reg->id)) 4209 - continue; 4210 - bt_set_frame_slot(bt, fr, i); 4211 - } 4212 - } 4213 - 4214 - return 0; 4215 3990 } 4216 3991 4217 3992 /* ··· 4255 4210 verbose(env, "mark_precise: frame%d: last_idx %d first_idx %d subseq_idx %d \n", 4256 4211 bt->frame, last_idx, first_idx, subseq_idx); 4257 4212 } 4258 - 4259 - /* If some register with scalar ID is marked as precise, 4260 - * make sure that all registers sharing this ID are also precise. 4261 - * This is needed to estimate effect of find_equal_scalars(). 4262 - * Do this at the last instruction of each state, 4263 - * bpf_reg_state::id fields are valid for these instructions. 4264 - * 4265 - * Allows to track precision in situation like below: 4266 - * 4267 - * r2 = unknown value 4268 - * ... 4269 - * --- state #0 --- 4270 - * ... 4271 - * r1 = r2 // r1 and r2 now share the same ID 4272 - * ... 4273 - * --- state #1 {r1.id = A, r2.id = A} --- 4274 - * ... 4275 - * if (r2 > 10) goto exit; // find_equal_scalars() assigns range to r1 4276 - * ... 4277 - * --- state #2 {r1.id = A, r2.id = A} --- 4278 - * r3 = r10 4279 - * r3 += r1 // need to mark both r1 and r2 4280 - */ 4281 - if (mark_precise_scalar_ids(env, st)) 4282 - return -EFAULT; 4283 4213 4284 4214 if (last_idx < 0) { 4285 4215 /* we are at the entry into subprog, which ··· 4476 4456 4477 4457 if (!src_reg->id && !tnum_is_const(src_reg->var_off)) 4478 4458 /* Ensure that src_reg has a valid ID that will be copied to 4479 - * dst_reg and then will be used by find_equal_scalars() to 4459 + * dst_reg and then will be used by sync_linked_regs() to 4480 4460 * propagate min/max range. 4481 4461 */ 4482 4462 src_reg->id = ++env->id_gen; ··· 4645 4625 } 4646 4626 4647 4627 if (insn_flags) 4648 - return push_jmp_history(env, env->cur_state, insn_flags); 4628 + return push_jmp_history(env, env->cur_state, insn_flags, 0); 4649 4629 return 0; 4650 4630 } 4651 4631 ··· 4950 4930 insn_flags = 0; /* we are not restoring spilled register */ 4951 4931 } 4952 4932 if (insn_flags) 4953 - return push_jmp_history(env, env->cur_state, insn_flags); 4933 + return push_jmp_history(env, env->cur_state, insn_flags, 0); 4954 4934 return 0; 4955 4935 } 4956 4936 ··· 14119 14099 u64 val = reg_const_value(src_reg, alu32); 14120 14100 14121 14101 if ((dst_reg->id & BPF_ADD_CONST) || 14122 - /* prevent overflow in find_equal_scalars() later */ 14102 + /* prevent overflow in sync_linked_regs() later */ 14123 14103 val > (u32)S32_MAX) { 14124 14104 /* 14125 14105 * If the register already went through rX += val ··· 14134 14114 } else { 14135 14115 /* 14136 14116 * Make sure ID is cleared otherwise dst_reg min/max could be 14137 - * incorrectly propagated into other registers by find_equal_scalars() 14117 + * incorrectly propagated into other registers by sync_linked_regs() 14138 14118 */ 14139 14119 dst_reg->id = 0; 14140 14120 } ··· 14284 14264 copy_register_state(dst_reg, src_reg); 14285 14265 /* Make sure ID is cleared if src_reg is not in u32 14286 14266 * range otherwise dst_reg min/max could be incorrectly 14287 - * propagated into src_reg by find_equal_scalars() 14267 + * propagated into src_reg by sync_linked_regs() 14288 14268 */ 14289 14269 if (!is_src_reg_u32) 14290 14270 dst_reg->id = 0; ··· 15107 15087 return true; 15108 15088 } 15109 15089 15110 - static void find_equal_scalars(struct bpf_verifier_state *vstate, 15111 - struct bpf_reg_state *known_reg) 15090 + static void __collect_linked_regs(struct linked_regs *reg_set, struct bpf_reg_state *reg, 15091 + u32 id, u32 frameno, u32 spi_or_reg, bool is_reg) 15092 + { 15093 + struct linked_reg *e; 15094 + 15095 + if (reg->type != SCALAR_VALUE || (reg->id & ~BPF_ADD_CONST) != id) 15096 + return; 15097 + 15098 + e = linked_regs_push(reg_set); 15099 + if (e) { 15100 + e->frameno = frameno; 15101 + e->is_reg = is_reg; 15102 + e->regno = spi_or_reg; 15103 + } else { 15104 + reg->id = 0; 15105 + } 15106 + } 15107 + 15108 + /* For all R being scalar registers or spilled scalar registers 15109 + * in verifier state, save R in linked_regs if R->id == id. 15110 + * If there are too many Rs sharing same id, reset id for leftover Rs. 15111 + */ 15112 + static void collect_linked_regs(struct bpf_verifier_state *vstate, u32 id, 15113 + struct linked_regs *linked_regs) 15114 + { 15115 + struct bpf_func_state *func; 15116 + struct bpf_reg_state *reg; 15117 + int i, j; 15118 + 15119 + id = id & ~BPF_ADD_CONST; 15120 + for (i = vstate->curframe; i >= 0; i--) { 15121 + func = vstate->frame[i]; 15122 + for (j = 0; j < BPF_REG_FP; j++) { 15123 + reg = &func->regs[j]; 15124 + __collect_linked_regs(linked_regs, reg, id, i, j, true); 15125 + } 15126 + for (j = 0; j < func->allocated_stack / BPF_REG_SIZE; j++) { 15127 + if (!is_spilled_reg(&func->stack[j])) 15128 + continue; 15129 + reg = &func->stack[j].spilled_ptr; 15130 + __collect_linked_regs(linked_regs, reg, id, i, j, false); 15131 + } 15132 + } 15133 + } 15134 + 15135 + /* For all R in linked_regs, copy known_reg range into R 15136 + * if R->id == known_reg->id. 15137 + */ 15138 + static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_state *known_reg, 15139 + struct linked_regs *linked_regs) 15112 15140 { 15113 15141 struct bpf_reg_state fake_reg; 15114 - struct bpf_func_state *state; 15115 15142 struct bpf_reg_state *reg; 15143 + struct linked_reg *e; 15144 + int i; 15116 15145 15117 - bpf_for_each_reg_in_vstate(vstate, state, reg, ({ 15146 + for (i = 0; i < linked_regs->cnt; ++i) { 15147 + e = &linked_regs->entries[i]; 15148 + reg = e->is_reg ? &vstate->frame[e->frameno]->regs[e->regno] 15149 + : &vstate->frame[e->frameno]->stack[e->spi].spilled_ptr; 15118 15150 if (reg->type != SCALAR_VALUE || reg == known_reg) 15119 15151 continue; 15120 15152 if ((reg->id & ~BPF_ADD_CONST) != (known_reg->id & ~BPF_ADD_CONST)) ··· 15184 15112 copy_register_state(reg, known_reg); 15185 15113 /* 15186 15114 * Must preserve off, id and add_const flag, 15187 - * otherwise another find_equal_scalars() will be incorrect. 15115 + * otherwise another sync_linked_regs() will be incorrect. 15188 15116 */ 15189 15117 reg->off = saved_off; 15190 15118 ··· 15192 15120 scalar_min_max_add(reg, &fake_reg); 15193 15121 reg->var_off = tnum_add(reg->var_off, fake_reg.var_off); 15194 15122 } 15195 - })); 15123 + } 15196 15124 } 15197 15125 15198 15126 static int check_cond_jmp_op(struct bpf_verifier_env *env, ··· 15203 15131 struct bpf_reg_state *regs = this_branch->frame[this_branch->curframe]->regs; 15204 15132 struct bpf_reg_state *dst_reg, *other_branch_regs, *src_reg = NULL; 15205 15133 struct bpf_reg_state *eq_branch_regs; 15134 + struct linked_regs linked_regs = {}; 15206 15135 u8 opcode = BPF_OP(insn->code); 15207 15136 bool is_jmp32; 15208 15137 int pred = -1; ··· 15318 15245 return 0; 15319 15246 } 15320 15247 15248 + /* Push scalar registers sharing same ID to jump history, 15249 + * do this before creating 'other_branch', so that both 15250 + * 'this_branch' and 'other_branch' share this history 15251 + * if parent state is created. 15252 + */ 15253 + if (BPF_SRC(insn->code) == BPF_X && src_reg->type == SCALAR_VALUE && src_reg->id) 15254 + collect_linked_regs(this_branch, src_reg->id, &linked_regs); 15255 + if (dst_reg->type == SCALAR_VALUE && dst_reg->id) 15256 + collect_linked_regs(this_branch, dst_reg->id, &linked_regs); 15257 + if (linked_regs.cnt > 1) { 15258 + err = push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs)); 15259 + if (err) 15260 + return err; 15261 + } 15262 + 15321 15263 other_branch = push_stack(env, *insn_idx + insn->off + 1, *insn_idx, 15322 15264 false); 15323 15265 if (!other_branch) ··· 15363 15275 if (BPF_SRC(insn->code) == BPF_X && 15364 15276 src_reg->type == SCALAR_VALUE && src_reg->id && 15365 15277 !WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) { 15366 - find_equal_scalars(this_branch, src_reg); 15367 - find_equal_scalars(other_branch, &other_branch_regs[insn->src_reg]); 15278 + sync_linked_regs(this_branch, src_reg, &linked_regs); 15279 + sync_linked_regs(other_branch, &other_branch_regs[insn->src_reg], &linked_regs); 15368 15280 } 15369 15281 if (dst_reg->type == SCALAR_VALUE && dst_reg->id && 15370 15282 !WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) { 15371 - find_equal_scalars(this_branch, dst_reg); 15372 - find_equal_scalars(other_branch, &other_branch_regs[insn->dst_reg]); 15283 + sync_linked_regs(this_branch, dst_reg, &linked_regs); 15284 + sync_linked_regs(other_branch, &other_branch_regs[insn->dst_reg], &linked_regs); 15373 15285 } 15374 15286 15375 15287 /* if one pointer register is compared to another pointer ··· 16858 16770 * 16859 16771 * First verification path is [1-6]: 16860 16772 * - at (4) same bpf_reg_state::id (b) would be assigned to r6 and r7; 16861 - * - at (5) r6 would be marked <= X, find_equal_scalars() would also mark 16773 + * - at (5) r6 would be marked <= X, sync_linked_regs() would also mark 16862 16774 * r7 <= X, because r6 and r7 share same id. 16863 16775 * Next verification path is [1-4, 6]. 16864 16776 * ··· 17651 17563 * the current state. 17652 17564 */ 17653 17565 if (is_jmp_point(env, env->insn_idx)) 17654 - err = err ? : push_jmp_history(env, cur, 0); 17566 + err = err ? : push_jmp_history(env, cur, 0, 0); 17655 17567 err = err ? : propagate_precision(env, &sl->state); 17656 17568 if (err) 17657 17569 return err; ··· 17919 17831 } 17920 17832 17921 17833 if (is_jmp_point(env, env->insn_idx)) { 17922 - err = push_jmp_history(env, state, 0); 17834 + err = push_jmp_history(env, state, 0, 0); 17923 17835 if (err) 17924 17836 return err; 17925 17837 }
+220 -116
tools/testing/selftests/bpf/progs/verifier_scalar_ids.c
··· 5 5 #include "bpf_misc.h" 6 6 7 7 /* Check that precision marks propagate through scalar IDs. 8 - * Registers r{0,1,2} have the same scalar ID at the moment when r0 is 9 - * marked to be precise, this mark is immediately propagated to r{1,2}. 8 + * Registers r{0,1,2} have the same scalar ID. 9 + * Range information is propagated for scalars sharing same ID. 10 + * Check that precision mark for r0 causes precision marks for r{1,2} 11 + * when range information is propagated for 'if <reg> <op> <const>' insn. 10 12 */ 11 13 SEC("socket") 12 14 __success __log_level(2) 13 - __msg("frame0: regs=r0,r1,r2 stack= before 4: (bf) r3 = r10") 14 - __msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0") 15 - __msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0") 16 - __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 17 - __msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns") 18 - __flag(BPF_F_TEST_STATE_FREQ) 19 - __naked void precision_same_state(void) 20 - { 21 - asm volatile ( 22 - /* r0 = random number up to 0xff */ 23 - "call %[bpf_ktime_get_ns];" 24 - "r0 &= 0xff;" 25 - /* tie r0.id == r1.id == r2.id */ 26 - "r1 = r0;" 27 - "r2 = r0;" 28 - /* force r0 to be precise, this immediately marks r1 and r2 as 29 - * precise as well because of shared IDs 30 - */ 31 - "r3 = r10;" 32 - "r3 += r0;" 33 - "r0 = 0;" 34 - "exit;" 35 - : 36 - : __imm(bpf_ktime_get_ns) 37 - : __clobber_all); 38 - } 39 - 40 - /* Same as precision_same_state, but mark propagates through state / 41 - * parent state boundary. 42 - */ 43 - SEC("socket") 44 - __success __log_level(2) 45 - __msg("frame0: last_idx 6 first_idx 5 subseq_idx -1") 46 - __msg("frame0: regs=r0,r1,r2 stack= before 5: (bf) r3 = r10") 15 + /* first 'if' branch */ 16 + __msg("6: (0f) r3 += r0") 17 + __msg("frame0: regs=r0 stack= before 4: (25) if r1 > 0x7 goto pc+0") 47 18 __msg("frame0: parent state regs=r0,r1,r2 stack=:") 48 - __msg("frame0: regs=r0,r1,r2 stack= before 4: (05) goto pc+0") 49 19 __msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0") 50 - __msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0") 51 - __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 52 - __msg("frame0: parent state regs=r0 stack=:") 53 - __msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns") 20 + /* second 'if' branch */ 21 + __msg("from 4 to 5: ") 22 + __msg("6: (0f) r3 += r0") 23 + __msg("frame0: regs=r0 stack= before 5: (bf) r3 = r10") 24 + __msg("frame0: regs=r0 stack= before 4: (25) if r1 > 0x7 goto pc+0") 25 + /* parent state already has r{0,1,2} as precise */ 26 + __msg("frame0: parent state regs= stack=:") 54 27 __flag(BPF_F_TEST_STATE_FREQ) 55 - __naked void precision_cross_state(void) 28 + __naked void linked_regs_bpf_k(void) 56 29 { 57 30 asm volatile ( 58 31 /* r0 = random number up to 0xff */ ··· 34 61 /* tie r0.id == r1.id == r2.id */ 35 62 "r1 = r0;" 36 63 "r2 = r0;" 37 - /* force checkpoint */ 38 - "goto +0;" 39 - /* force r0 to be precise, this immediately marks r1 and r2 as 64 + "if r1 > 7 goto +0;" 65 + /* force r0 to be precise, this eventually marks r1 and r2 as 40 66 * precise as well because of shared IDs 41 67 */ 42 68 "r3 = r10;" ··· 47 75 : __clobber_all); 48 76 } 49 77 50 - /* Same as precision_same_state, but break one of the 78 + /* Registers r{0,1,2} share same ID when 'if r1 > ...' insn is processed, 79 + * check that verifier marks r{1,2} as precise while backtracking 80 + * 'if r1 > ...' with r0 already marked. 81 + */ 82 + SEC("socket") 83 + __success __log_level(2) 84 + __flag(BPF_F_TEST_STATE_FREQ) 85 + __msg("frame0: regs=r0 stack= before 5: (2d) if r1 > r3 goto pc+0") 86 + __msg("frame0: parent state regs=r0,r1,r2,r3 stack=:") 87 + __msg("frame0: regs=r0,r1,r2,r3 stack= before 4: (b7) r3 = 7") 88 + __naked void linked_regs_bpf_x_src(void) 89 + { 90 + asm volatile ( 91 + /* r0 = random number up to 0xff */ 92 + "call %[bpf_ktime_get_ns];" 93 + "r0 &= 0xff;" 94 + /* tie r0.id == r1.id == r2.id */ 95 + "r1 = r0;" 96 + "r2 = r0;" 97 + "r3 = 7;" 98 + "if r1 > r3 goto +0;" 99 + /* force r0 to be precise, this eventually marks r1 and r2 as 100 + * precise as well because of shared IDs 101 + */ 102 + "r4 = r10;" 103 + "r4 += r0;" 104 + "r0 = 0;" 105 + "exit;" 106 + : 107 + : __imm(bpf_ktime_get_ns) 108 + : __clobber_all); 109 + } 110 + 111 + /* Registers r{0,1,2} share same ID when 'if r1 > r3' insn is processed, 112 + * check that verifier marks r{0,1,2} as precise while backtracking 113 + * 'if r1 > r3' with r3 already marked. 114 + */ 115 + SEC("socket") 116 + __success __log_level(2) 117 + __flag(BPF_F_TEST_STATE_FREQ) 118 + __msg("frame0: regs=r3 stack= before 5: (2d) if r1 > r3 goto pc+0") 119 + __msg("frame0: parent state regs=r0,r1,r2,r3 stack=:") 120 + __msg("frame0: regs=r0,r1,r2,r3 stack= before 4: (b7) r3 = 7") 121 + __naked void linked_regs_bpf_x_dst(void) 122 + { 123 + asm volatile ( 124 + /* r0 = random number up to 0xff */ 125 + "call %[bpf_ktime_get_ns];" 126 + "r0 &= 0xff;" 127 + /* tie r0.id == r1.id == r2.id */ 128 + "r1 = r0;" 129 + "r2 = r0;" 130 + "r3 = 7;" 131 + "if r1 > r3 goto +0;" 132 + /* force r0 to be precise, this eventually marks r1 and r2 as 133 + * precise as well because of shared IDs 134 + */ 135 + "r4 = r10;" 136 + "r4 += r3;" 137 + "r0 = 0;" 138 + "exit;" 139 + : 140 + : __imm(bpf_ktime_get_ns) 141 + : __clobber_all); 142 + } 143 + 144 + /* Same as linked_regs_bpf_k, but break one of the 51 145 * links, note that r1 is absent from regs=... in __msg below. 52 146 */ 53 147 SEC("socket") 54 148 __success __log_level(2) 55 - __msg("frame0: regs=r0,r2 stack= before 5: (bf) r3 = r10") 56 - __msg("frame0: regs=r0,r2 stack= before 4: (b7) r1 = 0") 57 - __msg("frame0: regs=r0,r2 stack= before 3: (bf) r2 = r0") 58 - __msg("frame0: regs=r0 stack= before 2: (bf) r1 = r0") 59 - __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 60 - __msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns") 149 + __msg("7: (0f) r3 += r0") 150 + __msg("frame0: regs=r0 stack= before 6: (bf) r3 = r10") 151 + __msg("frame0: parent state regs=r0 stack=:") 152 + __msg("frame0: regs=r0 stack= before 5: (25) if r0 > 0x7 goto pc+0") 153 + __msg("frame0: parent state regs=r0,r2 stack=:") 61 154 __flag(BPF_F_TEST_STATE_FREQ) 62 - __naked void precision_same_state_broken_link(void) 155 + __naked void linked_regs_broken_link(void) 63 156 { 64 157 asm volatile ( 65 158 /* r0 = random number up to 0xff */ ··· 137 100 * compared to the previous test 138 101 */ 139 102 "r1 = 0;" 140 - /* force r0 to be precise, this immediately marks r1 and r2 as 141 - * precise as well because of shared IDs 142 - */ 143 - "r3 = r10;" 144 - "r3 += r0;" 145 - "r0 = 0;" 146 - "exit;" 147 - : 148 - : __imm(bpf_ktime_get_ns) 149 - : __clobber_all); 150 - } 151 - 152 - /* Same as precision_same_state_broken_link, but with state / 153 - * parent state boundary. 154 - */ 155 - SEC("socket") 156 - __success __log_level(2) 157 - __msg("frame0: regs=r0,r2 stack= before 6: (bf) r3 = r10") 158 - __msg("frame0: regs=r0,r2 stack= before 5: (b7) r1 = 0") 159 - __msg("frame0: parent state regs=r0,r2 stack=:") 160 - __msg("frame0: regs=r0,r1,r2 stack= before 4: (05) goto pc+0") 161 - __msg("frame0: regs=r0,r1,r2 stack= before 3: (bf) r2 = r0") 162 - __msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0") 163 - __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 164 - __msg("frame0: parent state regs=r0 stack=:") 165 - __msg("frame0: regs=r0 stack= before 0: (85) call bpf_ktime_get_ns") 166 - __flag(BPF_F_TEST_STATE_FREQ) 167 - __naked void precision_cross_state_broken_link(void) 168 - { 169 - asm volatile ( 170 - /* r0 = random number up to 0xff */ 171 - "call %[bpf_ktime_get_ns];" 172 - "r0 &= 0xff;" 173 - /* tie r0.id == r1.id == r2.id */ 174 - "r1 = r0;" 175 - "r2 = r0;" 176 - /* force checkpoint, although link between r1 and r{0,2} is 177 - * broken by the next statement current precision tracking 178 - * algorithm can't react to it and propagates mark for r1 to 179 - * the parent state. 180 - */ 181 - "goto +0;" 182 - /* break link for r1, this is the only line that differs 183 - * compared to precision_cross_state() 184 - */ 185 - "r1 = 0;" 186 - /* force r0 to be precise, this immediately marks r1 and r2 as 187 - * precise as well because of shared IDs 103 + "if r0 > 7 goto +0;" 104 + /* force r0 to be precise, 105 + * this eventually marks r2 as precise because of shared IDs 188 106 */ 189 107 "r3 = r10;" 190 108 "r3 += r0;" ··· 156 164 */ 157 165 SEC("socket") 158 166 __success __log_level(2) 159 - __msg("11: (0f) r2 += r1") 167 + __msg("12: (0f) r2 += r1") 160 168 /* Current state */ 161 - __msg("frame2: last_idx 11 first_idx 10 subseq_idx -1") 162 - __msg("frame2: regs=r1 stack= before 10: (bf) r2 = r10") 169 + __msg("frame2: last_idx 12 first_idx 11 subseq_idx -1 ") 170 + __msg("frame2: regs=r1 stack= before 11: (bf) r2 = r10") 171 + __msg("frame2: parent state regs=r1 stack=") 172 + __msg("frame1: parent state regs= stack=") 173 + __msg("frame0: parent state regs= stack=") 174 + /* Parent state */ 175 + __msg("frame2: last_idx 10 first_idx 10 subseq_idx 11 ") 176 + __msg("frame2: regs=r1 stack= before 10: (25) if r1 > 0x7 goto pc+0") 163 177 __msg("frame2: parent state regs=r1 stack=") 164 178 /* frame1.r{6,7} are marked because mark_precise_scalar_ids() 165 179 * looks for all registers with frame2.r1.id in the current state ··· 190 192 __msg("frame0: parent state regs=r1,r6 stack=") 191 193 /* Parent state */ 192 194 __msg("frame0: last_idx 3 first_idx 1 subseq_idx 4") 193 - __msg("frame0: regs=r0,r1,r6 stack= before 3: (bf) r6 = r0") 195 + __msg("frame0: regs=r1,r6 stack= before 3: (bf) r6 = r0") 194 196 __msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0") 195 197 __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 196 198 __flag(BPF_F_TEST_STATE_FREQ) ··· 228 230 void precision_many_frames__bar(void) 229 231 { 230 232 asm volatile ( 231 - /* force r1 to be precise, this immediately marks: 233 + "if r1 > 7 goto +0;" 234 + /* force r1 to be precise, this eventually marks: 232 235 * - bar frame r1 233 236 * - foo frame r{1,6,7} 234 237 * - main frame r{1,6} ··· 246 247 */ 247 248 SEC("socket") 248 249 __success __log_level(2) 250 + __msg("11: (0f) r2 += r1") 249 251 /* foo frame */ 250 - __msg("frame1: regs=r1 stack=-8,-16 before 9: (bf) r2 = r10") 252 + __msg("frame1: regs=r1 stack= before 10: (bf) r2 = r10") 253 + __msg("frame1: regs=r1 stack= before 9: (25) if r1 > 0x7 goto pc+0") 251 254 __msg("frame1: regs=r1 stack=-8,-16 before 8: (7b) *(u64 *)(r10 -16) = r1") 252 255 __msg("frame1: regs=r1 stack=-8 before 7: (7b) *(u64 *)(r10 -8) = r1") 253 256 __msg("frame1: regs=r1 stack= before 4: (85) call pc+2") 254 257 /* main frame */ 255 - __msg("frame0: regs=r0,r1 stack=-8 before 3: (7b) *(u64 *)(r10 -8) = r1") 256 - __msg("frame0: regs=r0,r1 stack= before 2: (bf) r1 = r0") 258 + __msg("frame0: regs=r1 stack=-8 before 3: (7b) *(u64 *)(r10 -8) = r1") 259 + __msg("frame0: regs=r1 stack= before 2: (bf) r1 = r0") 257 260 __msg("frame0: regs=r0 stack= before 1: (57) r0 &= 255") 258 261 __flag(BPF_F_TEST_STATE_FREQ) 259 262 __naked void precision_stack(void) ··· 284 283 */ 285 284 "*(u64*)(r10 - 8) = r1;" 286 285 "*(u64*)(r10 - 16) = r1;" 287 - /* force r1 to be precise, this immediately marks: 286 + "if r1 > 7 goto +0;" 287 + /* force r1 to be precise, this eventually marks: 288 288 * - foo frame r1,fp{-8,-16} 289 289 * - main frame r1,fp{-8} 290 290 */ ··· 301 299 SEC("socket") 302 300 __success __log_level(2) 303 301 /* r{6,7} */ 304 - __msg("11: (0f) r3 += r7") 305 - __msg("frame0: regs=r6,r7 stack= before 10: (bf) r3 = r10") 302 + __msg("12: (0f) r3 += r7") 303 + __msg("frame0: regs=r7 stack= before 11: (bf) r3 = r10") 304 + __msg("frame0: regs=r7 stack= before 9: (25) if r7 > 0x7 goto pc+0") 306 305 /* ... skip some insns ... */ 307 306 __msg("frame0: regs=r6,r7 stack= before 3: (bf) r7 = r0") 308 307 __msg("frame0: regs=r0,r6 stack= before 2: (bf) r6 = r0") 309 308 /* r{8,9} */ 310 - __msg("12: (0f) r3 += r9") 311 - __msg("frame0: regs=r8,r9 stack= before 11: (0f) r3 += r7") 309 + __msg("13: (0f) r3 += r9") 310 + __msg("frame0: regs=r9 stack= before 12: (0f) r3 += r7") 312 311 /* ... skip some insns ... */ 312 + __msg("frame0: regs=r9 stack= before 10: (25) if r9 > 0x7 goto pc+0") 313 313 __msg("frame0: regs=r8,r9 stack= before 7: (bf) r9 = r0") 314 314 __msg("frame0: regs=r0,r8 stack= before 6: (bf) r8 = r0") 315 315 __flag(BPF_F_TEST_STATE_FREQ) ··· 332 328 "r9 = r0;" 333 329 /* clear r0 id */ 334 330 "r0 = 0;" 335 - /* force checkpoint */ 336 - "goto +0;" 331 + /* propagate equal scalars precision */ 332 + "if r7 > 7 goto +0;" 333 + "if r9 > 7 goto +0;" 337 334 "r3 = r10;" 338 335 /* force r7 to be precise, this also marks r6 */ 339 336 "r3 += r7;" 340 337 /* force r9 to be precise, this also marks r8 */ 341 338 "r3 += r9;" 339 + "exit;" 340 + : 341 + : __imm(bpf_ktime_get_ns) 342 + : __clobber_all); 343 + } 344 + 345 + SEC("socket") 346 + __success __log_level(2) 347 + __flag(BPF_F_TEST_STATE_FREQ) 348 + /* check thar r0 and r6 have different IDs after 'if', 349 + * collect_linked_regs() can't tie more than 6 registers for a single insn. 350 + */ 351 + __msg("8: (25) if r0 > 0x7 goto pc+0 ; R0=scalar(id=1") 352 + __msg("9: (bf) r6 = r6 ; R6_w=scalar(id=2") 353 + /* check that r{0-5} are marked precise after 'if' */ 354 + __msg("frame0: regs=r0 stack= before 8: (25) if r0 > 0x7 goto pc+0") 355 + __msg("frame0: parent state regs=r0,r1,r2,r3,r4,r5 stack=:") 356 + __naked void linked_regs_too_many_regs(void) 357 + { 358 + asm volatile ( 359 + /* r0 = random number up to 0xff */ 360 + "call %[bpf_ktime_get_ns];" 361 + "r0 &= 0xff;" 362 + /* tie r{0-6} IDs */ 363 + "r1 = r0;" 364 + "r2 = r0;" 365 + "r3 = r0;" 366 + "r4 = r0;" 367 + "r5 = r0;" 368 + "r6 = r0;" 369 + /* propagate range for r{0-6} */ 370 + "if r0 > 7 goto +0;" 371 + /* make r6 appear in the log */ 372 + "r6 = r6;" 373 + /* force r0 to be precise, 374 + * this would cause r{0-4} to be precise because of shared IDs 375 + */ 376 + "r7 = r10;" 377 + "r7 += r0;" 378 + "r0 = 0;" 379 + "exit;" 380 + : 381 + : __imm(bpf_ktime_get_ns) 382 + : __clobber_all); 383 + } 384 + 385 + SEC("socket") 386 + __failure __log_level(2) 387 + __flag(BPF_F_TEST_STATE_FREQ) 388 + __msg("regs=r7 stack= before 5: (3d) if r8 >= r0") 389 + __msg("parent state regs=r0,r7,r8") 390 + __msg("regs=r0,r7,r8 stack= before 4: (25) if r0 > 0x1") 391 + __msg("div by zero") 392 + __naked void linked_regs_broken_link_2(void) 393 + { 394 + asm volatile ( 395 + "call %[bpf_get_prandom_u32];" 396 + "r7 = r0;" 397 + "r8 = r0;" 398 + "call %[bpf_get_prandom_u32];" 399 + "if r0 > 1 goto +0;" 400 + /* r7.id == r8.id, 401 + * thus r7 precision implies r8 precision, 402 + * which implies r0 precision because of the conditional below. 403 + */ 404 + "if r8 >= r0 goto 1f;" 405 + /* break id relation between r7 and r8 */ 406 + "r8 += r8;" 407 + /* make r7 precise */ 408 + "if r7 == 0 goto 1f;" 409 + "r0 /= 0;" 410 + "1:" 411 + "r0 = 42;" 412 + "exit;" 413 + : 414 + : __imm(bpf_get_prandom_u32) 415 + : __clobber_all); 416 + } 417 + 418 + /* Check that mark_chain_precision() for one of the conditional jump 419 + * operands does not trigger equal scalars precision propagation. 420 + */ 421 + SEC("socket") 422 + __success __log_level(2) 423 + __msg("3: (25) if r1 > 0x100 goto pc+0") 424 + __msg("frame0: regs=r1 stack= before 2: (bf) r1 = r0") 425 + __naked void cjmp_no_linked_regs_trigger(void) 426 + { 427 + asm volatile ( 428 + /* r0 = random number up to 0xff */ 429 + "call %[bpf_ktime_get_ns];" 430 + "r0 &= 0xff;" 431 + /* tie r0.id == r1.id */ 432 + "r1 = r0;" 433 + /* the jump below would be predicted, thus r1 would be marked precise, 434 + * this should not imply precision mark for r0 435 + */ 436 + "if r1 > 256 goto +0;" 437 + "r0 = 0;" 342 438 "exit;" 343 439 : 344 440 : __imm(bpf_ktime_get_ns)
+8 -8
tools/testing/selftests/bpf/progs/verifier_spill_fill.c
··· 402 402 *(u32*)(r10 - 8) = r1; \ 403 403 /* 32-bit fill r2 from stack. */ \ 404 404 r2 = *(u32*)(r10 - 8); \ 405 - /* Compare r2 with another register to trigger find_equal_scalars.\ 405 + /* Compare r2 with another register to trigger sync_linked_regs.\ 406 406 * Having one random bit is important here, otherwise the verifier cuts\ 407 407 * the corners. If the ID was mistakenly preserved on spill, this would\ 408 408 * cause the verifier to think that r1 is also equal to zero in one of\ ··· 441 441 *(u16*)(r10 - 8) = r1; \ 442 442 /* 16-bit fill r2 from stack. */ \ 443 443 r2 = *(u16*)(r10 - 8); \ 444 - /* Compare r2 with another register to trigger find_equal_scalars.\ 444 + /* Compare r2 with another register to trigger sync_linked_regs.\ 445 445 * Having one random bit is important here, otherwise the verifier cuts\ 446 446 * the corners. If the ID was mistakenly preserved on spill, this would\ 447 447 * cause the verifier to think that r1 is also equal to zero in one of\ ··· 833 833 *(u64*)(r10 - 8) = r0; \ 834 834 /* 64-bit fill r1 from stack - should preserve the ID. */\ 835 835 r1 = *(u64*)(r10 - 8); \ 836 - /* Compare r1 with another register to trigger find_equal_scalars.\ 836 + /* Compare r1 with another register to trigger sync_linked_regs.\ 837 837 * Having one random bit is important here, otherwise the verifier cuts\ 838 838 * the corners. \ 839 839 */ \ ··· 866 866 *(u32*)(r10 - 8) = r0; \ 867 867 /* 32-bit fill r1 from stack - should preserve the ID. */\ 868 868 r1 = *(u32*)(r10 - 8); \ 869 - /* Compare r1 with another register to trigger find_equal_scalars.\ 869 + /* Compare r1 with another register to trigger sync_linked_regs.\ 870 870 * Having one random bit is important here, otherwise the verifier cuts\ 871 871 * the corners. \ 872 872 */ \ ··· 899 899 *(u16*)(r10 - 8) = r0; \ 900 900 /* 16-bit fill r1 from stack - should preserve the ID. */\ 901 901 r1 = *(u16*)(r10 - 8); \ 902 - /* Compare r1 with another register to trigger find_equal_scalars.\ 902 + /* Compare r1 with another register to trigger sync_linked_regs.\ 903 903 * Having one random bit is important here, otherwise the verifier cuts\ 904 904 * the corners. \ 905 905 */ \ ··· 932 932 *(u8*)(r10 - 8) = r0; \ 933 933 /* 8-bit fill r1 from stack - should preserve the ID. */\ 934 934 r1 = *(u8*)(r10 - 8); \ 935 - /* Compare r1 with another register to trigger find_equal_scalars.\ 935 + /* Compare r1 with another register to trigger sync_linked_regs.\ 936 936 * Having one random bit is important here, otherwise the verifier cuts\ 937 937 * the corners. \ 938 938 */ \ ··· 1029 1029 "r1 = *(u32*)(r10 - 4);" 1030 1030 #endif 1031 1031 " \ 1032 - /* Compare r1 with another register to trigger find_equal_scalars. */\ 1032 + /* Compare r1 with another register to trigger sync_linked_regs. */\ 1033 1033 r2 = 0; \ 1034 1034 if r1 != r2 goto l0_%=; \ 1035 1035 /* The result of this comparison is predefined. */\ ··· 1070 1070 "r2 = *(u32*)(r10 - 4);" 1071 1071 #endif 1072 1072 " \ 1073 - /* Compare r2 with another register to trigger find_equal_scalars.\ 1073 + /* Compare r2 with another register to trigger sync_linked_regs.\ 1074 1074 * Having one random bit is important here, otherwise the verifier cuts\ 1075 1075 * the corners. If the ID was mistakenly preserved on fill, this would\ 1076 1076 * cause the verifier to think that r1 is also equal to zero in one of\
+1 -1
tools/testing/selftests/bpf/progs/verifier_subprog_precision.c
··· 278 278 __msg("mark_precise: frame0: regs=r6 stack= before 13: (bf) r1 = r7") 279 279 __msg("mark_precise: frame0: regs=r6 stack= before 12: (27) r6 *= 4") 280 280 __msg("mark_precise: frame0: regs=r6 stack= before 11: (25) if r6 > 0x3 goto pc+4") 281 - __msg("mark_precise: frame0: regs=r6 stack= before 10: (bf) r6 = r0") 281 + __msg("mark_precise: frame0: regs=r0,r6 stack= before 10: (bf) r6 = r0") 282 282 __msg("mark_precise: frame0: regs=r0 stack= before 9: (85) call bpf_loop") 283 283 /* State entering callback body popped from states stack */ 284 284 __msg("from 9 to 17: frame1:")
+14 -14
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,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\ 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 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\ ··· 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,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=:\ 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=:\ 108 108 mark_precise: frame0: last_idx 20 first_idx 20\ 109 - mark_precise: frame0: regs=r2,r9 stack= before 20\ 109 + mark_precise: frame0: regs=r2 stack= before 20\ 110 110 mark_precise: frame0: parent state regs=r2,r9 stack=:\ 111 111 mark_precise: frame0: last_idx 19 first_idx 17\ 112 112 mark_precise: frame0: regs=r2,r9 stack= before 19\ ··· 183 183 .prog_type = BPF_PROG_TYPE_XDP, 184 184 .flags = BPF_F_TEST_STATE_FREQ, 185 185 .errstr = "mark_precise: frame0: last_idx 7 first_idx 7\ 186 - mark_precise: frame0: parent state regs=r4 stack=-8:\ 186 + mark_precise: frame0: parent state regs=r4 stack=:\ 187 187 mark_precise: frame0: last_idx 6 first_idx 4\ 188 - mark_precise: frame0: regs=r4 stack=-8 before 6: (b7) r0 = -1\ 189 - mark_precise: frame0: regs=r4 stack=-8 before 5: (79) r4 = *(u64 *)(r10 -8)\ 188 + mark_precise: frame0: regs=r4 stack= before 6: (b7) r0 = -1\ 189 + mark_precise: frame0: regs=r4 stack= before 5: (79) r4 = *(u64 *)(r10 -8)\ 190 190 mark_precise: frame0: regs= stack=-8 before 4: (7b) *(u64 *)(r3 -8) = r0\ 191 191 mark_precise: frame0: parent state regs=r0 stack=:\ 192 192 mark_precise: frame0: last_idx 3 first_idx 3\