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.

bpf: Only print scratched registers and stack slots to verifier logs.

When printing verifier state for any log level, print full verifier
state only on function calls or on errors. Otherwise, only print the
registers and stack slots that were accessed.

Log size differences:

verif_scale_loop6 before: 234566564
verif_scale_loop6 after: 72143943
69% size reduction

kfree_skb before: 166406
kfree_skb after: 55386
69% size reduction

Before:

156: (61) r0 = *(u32 *)(r1 +0)
157: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R1=ctx(id=0,off=0,imm=0) R2_w=invP0 R10=fp0 fp-8_w=00000000 fp-16_w=00\
000000 fp-24_w=00000000 fp-32_w=00000000 fp-40_w=00000000 fp-48_w=00000000 fp-56_w=00000000 fp-64_w=00000000 fp-72_w=00000000 fp-80_w=00000\
000 fp-88_w=00000000 fp-96_w=00000000 fp-104_w=00000000 fp-112_w=00000000 fp-120_w=00000000 fp-128_w=00000000 fp-136_w=00000000 fp-144_w=00\
000000 fp-152_w=00000000 fp-160_w=00000000 fp-168_w=00000000 fp-176_w=00000000 fp-184_w=00000000 fp-192_w=00000000 fp-200_w=00000000 fp-208\
_w=00000000 fp-216_w=00000000 fp-224_w=00000000 fp-232_w=00000000 fp-240_w=00000000 fp-248_w=00000000 fp-256_w=00000000 fp-264_w=00000000 f\
p-272_w=00000000 fp-280_w=00000000 fp-288_w=00000000 fp-296_w=00000000 fp-304_w=00000000 fp-312_w=00000000 fp-320_w=00000000 fp-328_w=00000\
000 fp-336_w=00000000 fp-344_w=00000000 fp-352_w=00000000 fp-360_w=00000000 fp-368_w=00000000 fp-376_w=00000000 fp-384_w=00000000 fp-392_w=\
00000000 fp-400_w=00000000 fp-408_w=00000000 fp-416_w=00000000 fp-424_w=00000000 fp-432_w=00000000 fp-440_w=00000000 fp-448_w=00000000
; return skb->len;
157: (95) exit
Func#4 is safe for any args that match its prototype
Validating get_constant() func#5...
158: R1=invP(id=0) R10=fp0
; int get_constant(long val)
158: (bf) r0 = r1
159: R0_w=invP(id=1) R1=invP(id=1) R10=fp0
; return val - 122;
159: (04) w0 += -122
160: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R1=invP(id=1) R10=fp0
; return val - 122;
160: (95) exit
Func#5 is safe for any args that match its prototype
Validating get_skb_ifindex() func#6...
161: R1=invP(id=0) R2=ctx(id=0,off=0,imm=0) R3=invP(id=0) R10=fp0
; int get_skb_ifindex(int val, struct __sk_buff *skb, int var)
161: (bc) w0 = w3
162: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R1=invP(id=0) R2=ctx(id=0,off=0,imm=0) R3=invP(id=0) R10=fp0

After:

156: (61) r0 = *(u32 *)(r1 +0)
157: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R1=ctx(id=0,off=0,imm=0)
; return skb->len;
157: (95) exit
Func#4 is safe for any args that match its prototype
Validating get_constant() func#5...
158: R1=invP(id=0) R10=fp0
; int get_constant(long val)
158: (bf) r0 = r1
159: R0_w=invP(id=1) R1=invP(id=1)
; return val - 122;
159: (04) w0 += -122
160: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff))
; return val - 122;
160: (95) exit
Func#5 is safe for any args that match its prototype
Validating get_skb_ifindex() func#6...
161: R1=invP(id=0) R2=ctx(id=0,off=0,imm=0) R3=invP(id=0) R10=fp0
; int get_skb_ifindex(int val, struct __sk_buff *skb, int var)
161: (bc) w0 = w3
162: R0_w=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R3=invP(id=0)

Signed-off-by: Christy Lee <christylee@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20211216213358.3374427-2-christylee@fb.com

authored by

Christy Lee and committed by
Alexei Starovoitov
0f55f9ed 4658e15d

+91 -29
+7
include/linux/bpf_verifier.h
··· 474 474 /* longest register parentage chain walked for liveness marking */ 475 475 u32 longest_mark_read_walk; 476 476 bpfptr_t fd_array; 477 + 478 + /* bit mask to keep track of whether a register has been accessed 479 + * since the last time the function state was printed 480 + */ 481 + u32 scratched_regs; 482 + /* Same as scratched_regs but for stack slots */ 483 + u64 scratched_stack_slots; 477 484 }; 478 485 479 486 __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
+69 -14
kernel/bpf/verifier.c
··· 609 609 return btf_name_by_offset(btf, btf_type_by_id(btf, id)->name_off); 610 610 } 611 611 612 + static void mark_reg_scratched(struct bpf_verifier_env *env, u32 regno) 613 + { 614 + env->scratched_regs |= 1U << regno; 615 + } 616 + 617 + static void mark_stack_slot_scratched(struct bpf_verifier_env *env, u32 spi) 618 + { 619 + env->scratched_stack_slots |= 1UL << spi; 620 + } 621 + 622 + static bool reg_scratched(const struct bpf_verifier_env *env, u32 regno) 623 + { 624 + return (env->scratched_regs >> regno) & 1; 625 + } 626 + 627 + static bool stack_slot_scratched(const struct bpf_verifier_env *env, u64 regno) 628 + { 629 + return (env->scratched_stack_slots >> regno) & 1; 630 + } 631 + 632 + static bool verifier_state_scratched(const struct bpf_verifier_env *env) 633 + { 634 + return env->scratched_regs || env->scratched_stack_slots; 635 + } 636 + 637 + static void mark_verifier_state_clean(struct bpf_verifier_env *env) 638 + { 639 + env->scratched_regs = 0U; 640 + env->scratched_stack_slots = 0UL; 641 + } 642 + 643 + /* Used for printing the entire verifier state. */ 644 + static void mark_verifier_state_scratched(struct bpf_verifier_env *env) 645 + { 646 + env->scratched_regs = ~0U; 647 + env->scratched_stack_slots = ~0UL; 648 + } 649 + 612 650 /* The reg state of a pointer or a bounded scalar was saved when 613 651 * it was spilled to the stack. 614 652 */ ··· 662 624 } 663 625 664 626 static void print_verifier_state(struct bpf_verifier_env *env, 665 - const struct bpf_func_state *state) 627 + const struct bpf_func_state *state, 628 + bool print_all) 666 629 { 667 630 const struct bpf_reg_state *reg; 668 631 enum bpf_reg_type t; ··· 675 636 reg = &state->regs[i]; 676 637 t = reg->type; 677 638 if (t == NOT_INIT) 639 + continue; 640 + if (!print_all && !reg_scratched(env, i)) 678 641 continue; 679 642 verbose(env, " R%d", i); 680 643 print_liveness(env, reg->live); ··· 767 726 types_buf[BPF_REG_SIZE] = 0; 768 727 if (!valid) 769 728 continue; 729 + if (!print_all && !stack_slot_scratched(env, i)) 730 + continue; 770 731 verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE); 771 732 print_liveness(env, state->stack[i].spilled_ptr.live); 772 733 if (is_spilled_reg(&state->stack[i])) { ··· 794 751 if (state->in_async_callback_fn) 795 752 verbose(env, " async_cb"); 796 753 verbose(env, "\n"); 754 + mark_verifier_state_clean(env); 797 755 } 798 756 799 757 /* copy array src of length n * size bytes to dst. dst is reallocated if it's too ··· 1585 1541 state->frameno = frameno; 1586 1542 state->subprogno = subprogno; 1587 1543 init_reg_state(env, state); 1544 + mark_verifier_state_scratched(env); 1588 1545 } 1589 1546 1590 1547 /* Similar to push_stack(), but for async callbacks */ ··· 2273 2228 return -EINVAL; 2274 2229 } 2275 2230 2231 + mark_reg_scratched(env, regno); 2232 + 2276 2233 reg = &regs[regno]; 2277 2234 rw64 = is_reg64(env, insn, regno, reg, t); 2278 2235 if (t == SRC_OP) { ··· 2725 2678 reg->precise = true; 2726 2679 } 2727 2680 if (env->log.level & BPF_LOG_LEVEL) { 2728 - print_verifier_state(env, func); 2681 + print_verifier_state(env, func, false); 2729 2682 verbose(env, "parent %s regs=%x stack=%llx marks\n", 2730 2683 new_marks ? "didn't have" : "already had", 2731 2684 reg_mask, stack_mask); ··· 2884 2837 env->insn_aux_data[insn_idx].sanitize_stack_spill = true; 2885 2838 } 2886 2839 2840 + mark_stack_slot_scratched(env, spi); 2887 2841 if (reg && !(off % BPF_REG_SIZE) && register_is_bounded(reg) && 2888 2842 !register_is_null(reg) && env->bpf_capable) { 2889 2843 if (dst_reg != BPF_REG_FP) { ··· 3006 2958 slot = -i - 1; 3007 2959 spi = slot / BPF_REG_SIZE; 3008 2960 stype = &state->stack[spi].slot_type[slot % BPF_REG_SIZE]; 2961 + mark_stack_slot_scratched(env, spi); 3009 2962 3010 2963 if (!env->allow_ptr_leaks 3011 2964 && *stype != NOT_INIT ··· 3425 3376 * to make sure our theoretical access will be safe. 3426 3377 */ 3427 3378 if (env->log.level & BPF_LOG_LEVEL) 3428 - print_verifier_state(env, state); 3379 + print_verifier_state(env, state, false); 3429 3380 3430 3381 /* The minimum value is only important with signed 3431 3382 * comparisons where we can't assume the floor of a ··· 6060 6011 6061 6012 if (env->log.level & BPF_LOG_LEVEL) { 6062 6013 verbose(env, "caller:\n"); 6063 - print_verifier_state(env, caller); 6014 + print_verifier_state(env, caller, true); 6064 6015 verbose(env, "callee:\n"); 6065 - print_verifier_state(env, callee); 6016 + print_verifier_state(env, callee, true); 6066 6017 } 6067 6018 return 0; 6068 6019 } ··· 6277 6228 *insn_idx = callee->callsite + 1; 6278 6229 if (env->log.level & BPF_LOG_LEVEL) { 6279 6230 verbose(env, "returning from callee:\n"); 6280 - print_verifier_state(env, callee); 6231 + print_verifier_state(env, callee, true); 6281 6232 verbose(env, "to caller at %d:\n", *insn_idx); 6282 - print_verifier_state(env, caller); 6233 + print_verifier_state(env, caller, true); 6283 6234 } 6284 6235 /* clear everything in the callee */ 6285 6236 free_func_state(callee); ··· 8298 8249 8299 8250 /* Got here implies adding two SCALAR_VALUEs */ 8300 8251 if (WARN_ON_ONCE(ptr_reg)) { 8301 - print_verifier_state(env, state); 8252 + print_verifier_state(env, state, true); 8302 8253 verbose(env, "verifier internal error: unexpected ptr_reg\n"); 8303 8254 return -EINVAL; 8304 8255 } 8305 8256 if (WARN_ON(!src_reg)) { 8306 - print_verifier_state(env, state); 8257 + print_verifier_state(env, state, true); 8307 8258 verbose(env, "verifier internal error: no src_reg\n"); 8308 8259 return -EINVAL; 8309 8260 } ··· 9438 9389 return -EACCES; 9439 9390 } 9440 9391 if (env->log.level & BPF_LOG_LEVEL) 9441 - print_verifier_state(env, this_branch->frame[this_branch->curframe]); 9392 + print_verifier_state(env, this_branch->frame[this_branch->curframe], false); 9442 9393 return 0; 9443 9394 } 9444 9395 ··· 11308 11259 11309 11260 if (env->log.level & BPF_LOG_LEVEL2 || 11310 11261 (env->log.level & BPF_LOG_LEVEL && do_print_state)) { 11311 - if (env->log.level & BPF_LOG_LEVEL2) 11312 - verbose(env, "%d:", env->insn_idx); 11313 - else 11262 + if (env->log.level & BPF_LOG_LEVEL2) { 11263 + if (verifier_state_scratched(env)) 11264 + verbose(env, "%d:", env->insn_idx); 11265 + } else { 11314 11266 verbose(env, "\nfrom %d to %d%s:", 11315 11267 env->prev_insn_idx, env->insn_idx, 11316 11268 env->cur_state->speculative ? 11317 11269 " (speculative execution)" : ""); 11318 - print_verifier_state(env, state->frame[state->curframe]); 11270 + } 11271 + print_verifier_state(env, state->frame[state->curframe], 11272 + false); 11319 11273 do_print_state = false; 11320 11274 } 11321 11275 ··· 11540 11488 if (err) 11541 11489 return err; 11542 11490 process_bpf_exit: 11491 + mark_verifier_state_scratched(env); 11543 11492 update_branch_counts(env, env->cur_state); 11544 11493 err = pop_stack(env, &prev_insn_idx, 11545 11494 &env->insn_idx, pop_log); ··· 14200 14147 goto err_unlock; 14201 14148 } 14202 14149 } 14150 + 14151 + mark_verifier_state_clean(env); 14203 14152 14204 14153 if (IS_ERR(btf_vmlinux)) { 14205 14154 /* Either gcc or pahole or kernel are broken. */
+15 -15
tools/testing/selftests/bpf/prog_tests/align.c
··· 39 39 }, 40 40 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 41 41 .matches = { 42 - {1, "R1=ctx(id=0,off=0,imm=0)"}, 43 - {1, "R10=fp0"}, 42 + {0, "R1=ctx(id=0,off=0,imm=0)"}, 43 + {0, "R10=fp0"}, 44 44 {1, "R3_w=inv2"}, 45 45 {2, "R3_w=inv4"}, 46 46 {3, "R3_w=inv8"}, ··· 67 67 }, 68 68 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 69 69 .matches = { 70 - {1, "R1=ctx(id=0,off=0,imm=0)"}, 71 - {1, "R10=fp0"}, 70 + {0, "R1=ctx(id=0,off=0,imm=0)"}, 71 + {0, "R10=fp0"}, 72 72 {1, "R3_w=inv1"}, 73 73 {2, "R3_w=inv2"}, 74 74 {3, "R3_w=inv4"}, ··· 96 96 }, 97 97 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 98 98 .matches = { 99 - {1, "R1=ctx(id=0,off=0,imm=0)"}, 100 - {1, "R10=fp0"}, 99 + {0, "R1=ctx(id=0,off=0,imm=0)"}, 100 + {0, "R10=fp0"}, 101 101 {1, "R3_w=inv4"}, 102 102 {2, "R3_w=inv8"}, 103 103 {3, "R3_w=inv10"}, ··· 118 118 }, 119 119 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 120 120 .matches = { 121 - {1, "R1=ctx(id=0,off=0,imm=0)"}, 122 - {1, "R10=fp0"}, 121 + {0, "R1=ctx(id=0,off=0,imm=0)"}, 122 + {0, "R10=fp0"}, 123 123 {1, "R3_w=inv7"}, 124 124 {2, "R3_w=inv7"}, 125 125 {3, "R3_w=inv14"}, ··· 161 161 }, 162 162 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 163 163 .matches = { 164 - {7, "R0_w=pkt(id=0,off=8,r=8,imm=0)"}, 164 + {6, "R0_w=pkt(id=0,off=8,r=8,imm=0)"}, 165 165 {7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, 166 166 {8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, 167 167 {9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, 168 168 {10, "R3_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, 169 169 {11, "R3_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, 170 - {18, "R3=pkt_end(id=0,off=0,imm=0)"}, 170 + {13, "R3_w=pkt_end(id=0,off=0,imm=0)"}, 171 171 {18, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, 172 172 {19, "R4_w=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"}, 173 173 {20, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, ··· 234 234 }, 235 235 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 236 236 .matches = { 237 - {4, "R5_w=pkt(id=0,off=0,r=0,imm=0)"}, 237 + {3, "R5_w=pkt(id=0,off=0,r=0,imm=0)"}, 238 238 {5, "R5_w=pkt(id=0,off=14,r=0,imm=0)"}, 239 239 {6, "R4_w=pkt(id=0,off=14,r=0,imm=0)"}, 240 - {10, "R2=pkt(id=0,off=0,r=18,imm=0)"}, 240 + {9, "R2=pkt(id=0,off=0,r=18,imm=0)"}, 241 241 {10, "R5=pkt(id=0,off=14,r=18,imm=0)"}, 242 242 {10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, 243 243 {14, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, ··· 296 296 /* Calculated offset in R6 has unknown value, but known 297 297 * alignment of 4. 298 298 */ 299 - {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, 299 + {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, 300 300 {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, 301 301 /* Offset is added to packet pointer R5, resulting in 302 302 * known fixed offset, and variable offset from R6. ··· 386 386 /* Calculated offset in R6 has unknown value, but known 387 387 * alignment of 4. 388 388 */ 389 - {8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, 389 + {6, "R2_w=pkt(id=0,off=0,r=8,imm=0)"}, 390 390 {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, 391 391 /* Adding 14 makes R6 be (4n+2) */ 392 392 {9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, ··· 458 458 /* Checked s>=0 */ 459 459 {9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, 460 460 /* packet pointer + nonnegative (4n+2) */ 461 - {11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, 461 + {12, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, 462 462 {13, "R4_w=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"}, 463 463 /* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine. 464 464 * We checked the bounds, but it might have been able