]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: Refactor jmp history to use dedicated spi/frame fields
authorYonghong Song <yonghong.song@linux.dev>
Wed, 13 May 2026 04:50:20 +0000 (21:50 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 13 May 2026 16:27:30 +0000 (09:27 -0700)
Move stack slot index (spi) and frame number out of the flags field
in bpf_jmp_history_entry into dedicated bitfields. This simplifies
the encoding and makes room for new flags.

Previously, spi and frame were packed into the lower 9 bits of the
12-bit flags field (3 bits frame + 6 bits spi), with INSN_F_STACK_ACCESS
at BIT(9) and INSN_F_DST/SRC_REG_STACK at BIT(10)/BIT(11).
But this has no room for an INSN_F_* flag for stack arguments.

To resolve this issue, bpf_jmp_history_entry field idx is narrowed to
20 bits (sufficient for insn indices up to 1M), and the freed bits hold
spi (6 bits) and frame (3 bits) as dedicated struct fields. The flags
enum is simplified accordingly:
  INSN_F_STACK_ACCESS  -> BIT(0)
  INSN_F_DST_REG_STACK -> BIT(1)
  INSN_F_SRC_REG_STACK -> BIT(2)
which allows more room for additional INSN_F_* flags.

bpf_push_jmp_history() now takes explicit spi and frame parameters
instead of encoding them into flags. The insn_stack_access_flags(),
insn_stack_access_spi(), and insn_stack_access_frameno() helpers are
removed.

No functional change.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20260513045020.2385962-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
include/linux/bpf_verifier.h
kernel/bpf/backtrack.c
kernel/bpf/states.c
kernel/bpf/verifier.c

index 5398a02a12802adc66f4a8f7b08b57e74be6f528..3ec3381699814de0167eb57c90d965fb935982ac 100644 (file)
@@ -435,40 +435,35 @@ struct bpf_func_state {
 
 #define MAX_CALL_FRAMES 8
 
-/* instruction history flags, used in bpf_jmp_history_entry.flags field */
+/* instruction history flags, used in bpf_jmp_history_entry.flags field.
+ * Frame number and SPI are stored in dedicated fields of bpf_jmp_history_entry.
+ */
 enum {
-       /* instruction references stack slot through PTR_TO_STACK register;
-        * we also store stack's frame number in lower 3 bits (MAX_CALL_FRAMES is 8)
-        * and accessed stack slot's index in next 6 bits (MAX_BPF_STACK is 512,
-        * 8 bytes per slot, so slot index (spi) is [0, 63])
-        */
-       INSN_F_FRAMENO_MASK = 0x7, /* 3 bits */
-
-       INSN_F_SPI_MASK = 0x3f, /* 6 bits */
-       INSN_F_SPI_SHIFT = 3, /* shifted 3 bits to the left */
+       INSN_F_STACK_ACCESS = BIT(0),
 
-       INSN_F_STACK_ACCESS = BIT(9),
-
-       INSN_F_DST_REG_STACK = BIT(10), /* dst_reg is PTR_TO_STACK */
-       INSN_F_SRC_REG_STACK = BIT(11), /* src_reg is PTR_TO_STACK */
-       /* total 12 bits are used now. */
+       INSN_F_DST_REG_STACK = BIT(1), /* dst_reg is PTR_TO_STACK */
+       INSN_F_SRC_REG_STACK = BIT(2), /* src_reg is PTR_TO_STACK */
 };
 
-static_assert(INSN_F_FRAMENO_MASK + 1 >= MAX_CALL_FRAMES);
-static_assert(INSN_F_SPI_MASK + 1 >= MAX_BPF_STACK / 8);
-
 struct bpf_jmp_history_entry {
-       u32 idx;
        /* insn idx can't be bigger than 1 million */
+       u32 idx : 20;
+       u32 frame : 3;  /* stack access frame number */
+       u32 spi : 6;    /* stack slot index (0..63) */
+       u32 : 3;
        u32 prev_idx : 20;
        /* special INSN_F_xxx flags */
-       u32 flags : 12;
+       u32 flags : 4;
+       u32 : 8;
        /* additional registers that need precision tracking when this
         * jump is backtracked, vector of six 10-bit records
         */
        u64 linked_regs;
 };
 
+static_assert(MAX_CALL_FRAMES <= (1 << 3));
+static_assert(MAX_BPF_STACK / 8 <= (1 << 6));
+
 /* Maximum number of bpf_reg_state objects that can exist at once */
 #define MAX_STACK_ARG_SLOTS (MAX_BPF_FUNC_ARGS - MAX_BPF_FUNC_REG_ARGS)
 #define BPF_ID_MAP_SIZE ((MAX_BPF_REG + MAX_BPF_STACK / BPF_REG_SIZE + \
@@ -1198,7 +1193,7 @@ struct list_head *bpf_explored_state(struct bpf_verifier_env *env, int idx);
 void bpf_free_verifier_state(struct bpf_verifier_state *state, bool free_self);
 void bpf_free_backedges(struct bpf_scc_visit *visit);
 int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
-                        int insn_flags, u64 linked_regs);
+                        int insn_flags, int spi, int frame, u64 linked_regs);
 void bpf_bt_sync_linked_regs(struct backtrack_state *bt, struct bpf_jmp_history_entry *hist);
 void bpf_mark_reg_not_init(const struct bpf_verifier_env *env,
                           struct bpf_reg_state *reg);
index 854731dc93fe7925aa80a20d429a8b55af07c18e..5e93e57fb7ae8be81a50f76e758a8adf082e21ab 100644 (file)
@@ -9,7 +9,7 @@
 
 /* for any branch, call, exit record the history of jmps in the given state */
 int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state *cur,
-                        int insn_flags, u64 linked_regs)
+                        int insn_flags, int spi, int frame, u64 linked_regs)
 {
        u32 cnt = cur->jmp_history_cnt;
        struct bpf_jmp_history_entry *p;
@@ -25,6 +25,8 @@ int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state
                                env, "insn history: insn_idx %d cur flags %x new flags %x",
                                env->insn_idx, env->cur_hist_ent->flags, insn_flags);
                env->cur_hist_ent->flags |= insn_flags;
+               env->cur_hist_ent->spi = spi;
+               env->cur_hist_ent->frame = frame;
                verifier_bug_if(env->cur_hist_ent->linked_regs != 0, env,
                                "insn history: insn_idx %d linked_regs: %#llx",
                                env->insn_idx, env->cur_hist_ent->linked_regs);
@@ -43,6 +45,8 @@ int bpf_push_jmp_history(struct bpf_verifier_env *env, struct bpf_verifier_state
        p->idx = env->insn_idx;
        p->prev_idx = env->prev_insn_idx;
        p->flags = insn_flags;
+       p->spi = spi;
+       p->frame = frame;
        p->linked_regs = linked_regs;
        cur->jmp_history_cnt = cnt;
        env->cur_hist_ent = p;
@@ -64,16 +68,6 @@ static bool is_atomic_fetch_insn(const struct bpf_insn *insn)
               (insn->imm & BPF_FETCH);
 }
 
-static int insn_stack_access_spi(int insn_flags)
-{
-       return (insn_flags >> INSN_F_SPI_SHIFT) & INSN_F_SPI_MASK;
-}
-
-static int insn_stack_access_frameno(int insn_flags)
-{
-       return insn_flags & INSN_F_FRAMENO_MASK;
-}
-
 /* Backtrack one insn at a time. If idx is not at the top of recorded
  * history then previous instruction came from straight line execution.
  * Return -ENOENT if we exhausted all instructions within given state.
@@ -353,8 +347,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
                 * that [fp - off] slot contains scalar that needs to be
                 * tracked with precision
                 */
-               spi = insn_stack_access_spi(hist->flags);
-               fr = insn_stack_access_frameno(hist->flags);
+               spi = hist->spi;
+               fr = hist->frame;
                bpf_bt_set_frame_slot(bt, fr, spi);
        } else if (class == BPF_STX || class == BPF_ST) {
                if (bt_is_reg_set(bt, dreg))
@@ -366,8 +360,8 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
                /* scalars can only be spilled into stack */
                if (!hist || !(hist->flags & INSN_F_STACK_ACCESS))
                        return 0;
-               spi = insn_stack_access_spi(hist->flags);
-               fr = insn_stack_access_frameno(hist->flags);
+               spi = hist->spi;
+               fr = hist->frame;
                if (!bt_is_frame_slot_set(bt, fr, spi))
                        return 0;
                bt_clear_frame_slot(bt, fr, spi);
index 3ce6d2652b27c22a0f68bdcfe9c6dd856de9d6cc..87733813600950da80f05f8ceba6ceac9478132c 100644 (file)
@@ -1403,7 +1403,7 @@ hit:
                         */
                        err = 0;
                        if (bpf_is_jmp_point(env, env->insn_idx))
-                               err = bpf_push_jmp_history(env, cur, 0, 0);
+                               err = bpf_push_jmp_history(env, cur, 0, 0, 0, 0);
                        err = err ? : propagate_precision(env, &sl->state, cur, NULL);
                        if (err)
                                return err;
index a29b3003cbeca5d59f661146cbb3e766bfb6bed4..d15aef2fe4a1d720fab96164f28003f5a20d60aa 100644 (file)
@@ -3198,11 +3198,6 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
        return __check_reg_arg(env, state->regs, regno, t);
 }
 
-static int insn_stack_access_flags(int frameno, int spi)
-{
-       return INSN_F_STACK_ACCESS | (spi << INSN_F_SPI_SHIFT) | frameno;
-}
-
 static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
 {
        env->insn_aux_data[idx].indirect_target = true;
@@ -3517,7 +3512,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
        int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err;
        struct bpf_insn *insn = &env->prog->insnsi[insn_idx];
        struct bpf_reg_state *reg = NULL;
-       int insn_flags = insn_stack_access_flags(state->frameno, spi);
+       int insn_flags = INSN_F_STACK_ACCESS;
+       int hist_spi = spi, hist_frame = state->frameno;
 
        /* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
         * so it's aligned access and [off, off + size) are within stack limits
@@ -3613,7 +3609,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
        }
 
        if (insn_flags)
-               return bpf_push_jmp_history(env, env->cur_state, insn_flags, 0);
+               return bpf_push_jmp_history(env, env->cur_state, insn_flags,
+                                           hist_spi, hist_frame, 0);
        return 0;
 }
 
@@ -3809,7 +3806,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
        int i, slot = -off - 1, spi = slot / BPF_REG_SIZE;
        struct bpf_reg_state *reg;
        u8 *stype, type;
-       int insn_flags = insn_stack_access_flags(reg_state->frameno, spi);
+       int insn_flags = INSN_F_STACK_ACCESS;
+       int hist_spi = spi, hist_frame = reg_state->frameno;
 
        stype = reg_state->stack[spi].slot_type;
        reg = &reg_state->stack[spi].spilled_ptr;
@@ -3940,7 +3938,8 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
                insn_flags = 0; /* we are not restoring spilled register */
        }
        if (insn_flags)
-               return bpf_push_jmp_history(env, env->cur_state, insn_flags, 0);
+               return bpf_push_jmp_history(env, env->cur_state, insn_flags,
+                                           hist_spi, hist_frame, 0);
        return 0;
 }
 
@@ -15907,7 +15906,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
        }
 
        if (insn_flags) {
-               err = bpf_push_jmp_history(env, this_branch, insn_flags, 0);
+               err = bpf_push_jmp_history(env, this_branch, insn_flags, 0, 0, 0);
                if (err)
                        return err;
        }
@@ -15971,7 +15970,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
        if (dst_reg->type == SCALAR_VALUE && dst_reg->id)
                collect_linked_regs(env, this_branch, dst_reg->id, &linked_regs);
        if (linked_regs.cnt > 1) {
-               err = bpf_push_jmp_history(env, this_branch, 0, linked_regs_pack(&linked_regs));
+               err = bpf_push_jmp_history(env, this_branch, 0, 0, 0, linked_regs_pack(&linked_regs));
                if (err)
                        return err;
        }
@@ -17278,7 +17277,7 @@ static int do_check(struct bpf_verifier_env *env)
                }
 
                if (bpf_is_jmp_point(env, env->insn_idx)) {
-                       err = bpf_push_jmp_history(env, state, 0, 0);
+                       err = bpf_push_jmp_history(env, state, 0, 0, 0, 0);
                        if (err)
                                return err;
                }