]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: bpf_verifier_state->cleaned flag instead of REG_LIVE_DONE
authorEduard Zingerman <eddyz87@gmail.com>
Fri, 19 Sep 2025 02:18:34 +0000 (19:18 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 19 Sep 2025 16:27:22 +0000 (09:27 -0700)
Prepare for bpf_reg_state->live field removal by introducing a
separate flag to track if clean_verifier_state() had been applied to
the state. No functional changes.

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20250918-callchain-sensitive-liveness-v3-1-c3cd27bacc60@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf_verifier.h
kernel/bpf/log.c
kernel/bpf/verifier.c

index 020de62bd09cdd6e4692354192c747f32e7cc323..ac16da8b49dc1c1e2ba371df785ca32aa7c5415a 100644 (file)
@@ -45,7 +45,6 @@ enum bpf_reg_liveness {
        REG_LIVE_READ64 = 0x2, /* likewise, but full 64-bit content matters */
        REG_LIVE_READ = REG_LIVE_READ32 | REG_LIVE_READ64,
        REG_LIVE_WRITTEN = 0x4, /* reg was written first, screening off later reads */
-       REG_LIVE_DONE = 0x8, /* liveness won't be updating this register anymore */
 };
 
 #define ITER_PREFIX "bpf_iter_"
@@ -445,6 +444,7 @@ struct bpf_verifier_state {
 
        bool speculative;
        bool in_sleepable;
+       bool cleaned;
 
        /* first and last insn idx of this verifier state */
        u32 first_insn_idx;
index e4983c1303e76003ba971a71c5ff6b0c9aa751af..0d6d7bfb2fd05e3870c4741f657257187158fff6 100644 (file)
@@ -545,14 +545,12 @@ static char slot_type_char[] = {
 static void print_liveness(struct bpf_verifier_env *env,
                           enum bpf_reg_liveness live)
 {
-       if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
-           verbose(env, "_");
+       if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN))
+               verbose(env, "_");
        if (live & REG_LIVE_READ)
                verbose(env, "r");
        if (live & REG_LIVE_WRITTEN)
                verbose(env, "w");
-       if (live & REG_LIVE_DONE)
-               verbose(env, "D");
 }
 
 #define UNUM_MAX_DECIMAL U16_MAX
index aef6b266f08d1f913d6f789fccea86776fb8355a..47cec5c8abff79ab9bd658de201c3fa5901fa664 100644 (file)
@@ -1758,6 +1758,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
                return err;
        dst_state->speculative = src->speculative;
        dst_state->in_sleepable = src->in_sleepable;
+       dst_state->cleaned = src->cleaned;
        dst_state->curframe = src->curframe;
        dst_state->branches = src->branches;
        dst_state->parent = src->parent;
@@ -3589,11 +3590,6 @@ static int mark_reg_read(struct bpf_verifier_env *env,
                /* if read wasn't screened by an earlier write ... */
                if (writes && state->live & REG_LIVE_WRITTEN)
                        break;
-               if (verifier_bug_if(parent->live & REG_LIVE_DONE, env,
-                                   "type %s var_off %lld off %d",
-                                   reg_type_str(env, parent->type),
-                                   parent->var_off.value, parent->off))
-                       return -EFAULT;
                /* The first condition is more likely to be true than the
                 * second, checked it first.
                 */
@@ -18501,7 +18497,6 @@ static void clean_func_state(struct bpf_verifier_env *env,
        for (i = 0; i < BPF_REG_FP; i++) {
                live = st->regs[i].live;
                /* liveness must not touch this register anymore */
-               st->regs[i].live |= REG_LIVE_DONE;
                if (!(live & REG_LIVE_READ))
                        /* since the register is unused, clear its state
                         * to make further comparison simpler
@@ -18512,7 +18507,6 @@ static void clean_func_state(struct bpf_verifier_env *env,
        for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
                live = st->stack[i].spilled_ptr.live;
                /* liveness must not touch this stack slot anymore */
-               st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
                if (!(live & REG_LIVE_READ)) {
                        __mark_reg_not_init(env, &st->stack[i].spilled_ptr);
                        for (j = 0; j < BPF_REG_SIZE; j++)
@@ -18526,6 +18520,7 @@ static void clean_verifier_state(struct bpf_verifier_env *env,
 {
        int i;
 
+       st->cleaned = true;
        for (i = 0; i <= st->curframe; i++)
                clean_func_state(env, st->frame[i]);
 }
@@ -18553,7 +18548,7 @@ static void clean_verifier_state(struct bpf_verifier_env *env,
  * their final liveness marks are already propagated.
  * Hence when the verifier completes the search of state list in is_state_visited()
  * we can call this clean_live_states() function to mark all liveness states
- * as REG_LIVE_DONE to indicate that 'parent' pointers of 'struct bpf_reg_state'
+ * as st->cleaned to indicate that 'parent' pointers of 'struct bpf_reg_state'
  * will not be used.
  * This function also clears the registers and stack for states that !READ
  * to simplify state merging.
@@ -18576,7 +18571,7 @@ static void clean_live_states(struct bpf_verifier_env *env, int insn,
                if (sl->state.insn_idx != insn ||
                    !same_callsites(&sl->state, cur))
                        continue;
-               if (sl->state.frame[0]->regs[0].live & REG_LIVE_DONE)
+               if (sl->state.cleaned)
                        /* all regs in this state in all frames were already marked */
                        continue;
                if (incomplete_read_marks(env, &sl->state))