]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bpf: print spilled register state in stack slot
authorAndrii Nakryiko <andrii@kernel.org>
Sat, 18 Nov 2023 03:46:19 +0000 (19:46 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 18 Nov 2023 19:39:59 +0000 (11:39 -0800)
Print the same register state representation when printing stack state,
as we do for normal registers. Note that if stack slot contains
subregister spill (1, 2, or 4 byte long), we'll still emit "m0?" mask
for those bytes that are not part of spilled register.

While means we can get something like fp-8=0000scalar() for a 4-byte
spill with other 4 bytes still being STACK_ZERO.

Some example before and after, taken from the log of
pyperf_subprogs.bpf.o:

49: (7b) *(u64 *)(r10 -256) = r1      ; frame1: R1_w=ctx(off=0,imm=0) R10=fp0 fp-256_w=ctx
49: (7b) *(u64 *)(r10 -256) = r1      ; frame1: R1_w=ctx(off=0,imm=0) R10=fp0 fp-256_w=ctx(off=0,imm=0)

150: (7b) *(u64 *)(r10 -264) = r0     ; frame1: R0_w=map_value_or_null(id=6,off=0,ks=192,vs=4,imm=0) R10=fp0 fp-264_w=map_value_or_null
150: (7b) *(u64 *)(r10 -264) = r0     ; frame1: R0_w=map_value_or_null(id=6,off=0,ks=192,vs=4,imm=0) R10=fp0 fp-264_w=map_value_or_null(id=6,off=0,ks=192,vs=4,imm=0)

5192: (61) r1 = *(u32 *)(r10 -272)    ; frame1: R1_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=15,var_off=(0x0; 0xf)) R10=fp0 fp-272=
5192: (61) r1 = *(u32 *)(r10 -272)    ; frame1: R1_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=15,var_off=(0x0; 0xf)) R10=fp0 fp-272=????scalar(smin=smin32=0,smax=umax=smax32=umax32=15,var_off=(0x0; 0xf))

While at it, do a few other simple clean ups:
  - skip slot if it's not scratched before detecting whether it's valid;
  - move taking spilled_reg pointer outside of switch (only DYNPTR has
    to adjust that to get to the "main" slot);
  - don't recalculate types_buf second time for MISC/ZERO/default case.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231118034623.3320920-5-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/log.c

index 05d737e2fab3af53a8ae8942fea7ea66b9f6e150..97a1641e848e108aedcb7319c249f5b6fffeec43 100644 (file)
@@ -618,7 +618,6 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
                          bool print_all)
 {
        const struct bpf_reg_state *reg;
-       enum bpf_reg_type t;
        int i;
 
        if (state->frameno)
@@ -637,32 +636,38 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
        for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
                char types_buf[BPF_REG_SIZE + 1];
                bool valid = false;
+               u8 slot_type;
                int j;
 
+               if (!print_all && !stack_slot_scratched(env, i))
+                       continue;
+
                for (j = 0; j < BPF_REG_SIZE; j++) {
-                       if (state->stack[i].slot_type[j] != STACK_INVALID)
+                       slot_type = state->stack[i].slot_type[j];
+                       if (slot_type != STACK_INVALID)
                                valid = true;
-                       types_buf[j] = slot_type_char[state->stack[i].slot_type[j]];
+                       types_buf[j] = slot_type_char[slot_type];
                }
                types_buf[BPF_REG_SIZE] = 0;
                if (!valid)
                        continue;
-               if (!print_all && !stack_slot_scratched(env, i))
-                       continue;
+
+               reg = &state->stack[i].spilled_ptr;
                switch (state->stack[i].slot_type[BPF_REG_SIZE - 1]) {
                case STACK_SPILL:
-                       reg = &state->stack[i].spilled_ptr;
-                       t = reg->type;
+                       /* print MISC/ZERO/INVALID slots above subreg spill */
+                       for (j = 0; j < BPF_REG_SIZE; j++)
+                               if (state->stack[i].slot_type[j] == STACK_SPILL)
+                                       break;
+                       types_buf[j] = '\0';
 
                        verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
                        print_liveness(env, reg->live);
-                       verbose(env, "=%s", t == SCALAR_VALUE ? "" : reg_type_str(env, t));
-                       if (t == SCALAR_VALUE && reg->precise)
-                               verbose(env, "P");
-                       if (t == SCALAR_VALUE && tnum_is_const(reg->var_off))
-                               verbose(env, "%lld", reg->var_off.value + reg->off);
+                       verbose(env, "=%s", types_buf);
+                       print_reg_state(env, reg);
                        break;
                case STACK_DYNPTR:
+                       /* skip to main dynptr slot */
                        i += BPF_DYNPTR_NR_SLOTS - 1;
                        reg = &state->stack[i].spilled_ptr;
 
@@ -674,7 +679,6 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
                        break;
                case STACK_ITER:
                        /* only main slot has ref_obj_id set; skip others */
-                       reg = &state->stack[i].spilled_ptr;
                        if (!reg->ref_obj_id)
                                continue;
 
@@ -688,12 +692,6 @@ void print_verifier_state(struct bpf_verifier_env *env, const struct bpf_func_st
                case STACK_MISC:
                case STACK_ZERO:
                default:
-                       reg = &state->stack[i].spilled_ptr;
-
-                       for (j = 0; j < BPF_REG_SIZE; j++)
-                               types_buf[j] = slot_type_char[state->stack[i].slot_type[j]];
-                       types_buf[BPF_REG_SIZE] = 0;
-
                        verbose(env, " fp%d", (-i - 1) * BPF_REG_SIZE);
                        print_liveness(env, reg->live);
                        verbose(env, "=%s", types_buf);