From: Xu Kuohai <xukuohai@huawei.com>
Introduce helper bpf_insn_is_indirect_target to check whether a BPF
instruction is an indirect jump target.
Since the verifier knows which instructions are indirect jump targets,
add a new flag indirect_target to struct bpf_insn_aux_data to mark
them. The verifier sets this flag when verifing an indirect jump target
instruction, and the helper checks it to determine whether an
instruction is an indirect jump target.
Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
---
include/linux/bpf.h | 2 ++
include/linux/bpf_verifier.h | 9 +++++----
kernel/bpf/core.c | 19 +++++++++++++++++++
kernel/bpf/verifier.c | 7 +++++++
4 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 05b34a6355b0..90760e250865 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1541,6 +1541,8 @@ bool bpf_has_frame_pointer(unsigned long ip);
int bpf_jit_charge_modmem(u32 size);
void bpf_jit_uncharge_modmem(u32 size);
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
+bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog,
+ int insn_idx);
#else
static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 090aa26d1c98..6431b94746cf 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -578,16 +578,17 @@ struct bpf_insn_aux_data {
/* below fields are initialized once */
unsigned int orig_idx; /* original instruction index */
- bool jmp_point;
- bool prune_point;
+ u32 jmp_point:1;
+ u32 prune_point:1;
/* ensure we check state equivalence and save state checkpoint and
* this instruction, regardless of any heuristics
*/
- bool force_checkpoint;
+ u32 force_checkpoint:1;
/* true if instruction is a call to a helper function that
* accepts callback function as a parameter.
*/
- bool calls_callback;
+ u32 calls_callback:1;
+ u32 indirect_target:1; /* if it is an indirect jump target */
/*
* CFG strongly connected component this instruction belongs to,
* zero if it is a singleton SCC.
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 88a1834030a0..51438ab3ba65 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1475,11 +1475,30 @@ int bpf_jit_blind_constants(struct bpf_verifier_env *env)
insn = prog->insnsi + i + insn_delta;
insn_cnt += insn_delta;
i += insn_delta;
+
+ /* bpf_patch_insn_data() calls adjust_insn_aux_data() to adjust insn_aux_data. The
+ * indirect_target flag for the original instruction is moved to the last of the new
+ * instructions, but the indirect jump target is actually the first one, so move
+ * it back.
+ */
+ if (env->insn_aux_data[i].indirect_target) {
+ env->insn_aux_data[i].indirect_target = 0;
+ env->insn_aux_data[i - insn_delta].indirect_target = 1;
+ }
}
prog->blinded = 1;
return 0;
}
+
+bool bpf_insn_is_indirect_target(const struct bpf_verifier_env *env, const struct bpf_prog *prog,
+ int insn_idx)
+{
+ if (!env)
+ return false;
+ insn_idx += prog->aux->subprog_start;
+ return env->insn_aux_data[insn_idx].indirect_target;
+}
#endif /* CONFIG_BPF_JIT */
/* Base function for offset calculation. Needs to go into .text section,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index f80bb8f0e00e..872d031338d1 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3998,6 +3998,11 @@ static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx)
return env->insn_aux_data[insn_idx].jmp_point;
}
+static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
+{
+ env->insn_aux_data[idx].indirect_target = true;
+}
+
#define LR_FRAMENO_BITS 3
#define LR_SPI_BITS 6
#define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1)
@@ -21051,12 +21056,14 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in
}
for (i = 0; i < n - 1; i++) {
+ mark_indirect_target(env, env->gotox_tmp_buf->items[i]);
other_branch = push_stack(env, env->gotox_tmp_buf->items[i],
env->insn_idx, env->cur_state->speculative);
if (IS_ERR(other_branch))
return PTR_ERR(other_branch);
}
env->insn_idx = env->gotox_tmp_buf->items[n-1];
+ mark_indirect_target(env, env->insn_idx);
return 0;
}
--
2.47.3
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c
> @@ -1475,11 +1475,30 @@ int bpf_jit_blind_constants(struct bpf_verifier_env *env)
> insn = prog->insnsi + i + insn_delta;
> insn_cnt += insn_delta;
> i += insn_delta;
> +
> + /* bpf_patch_insn_data() calls adjust_insn_aux_data() to adjust insn_aux_data. The
> + * indirect_target flag for the original instruction is moved to the last of the new
> + * instructions, but the indirect jump target is actually the first one, so move
> + * it back.
> + */
> + if (env->insn_aux_data[i].indirect_target) {
> + env->insn_aux_data[i].indirect_target = 0;
> + env->insn_aux_data[i - insn_delta].indirect_target = 1;
> + }
This fixup handles the displacement caused by
bpf_jit_blind_constants() itself, but adjust_insn_aux_data() has
the same displacement behavior in every caller of
bpf_patch_insn_data(). Several patching passes run before
bpf_jit_blind_constants() in bpf_check():
convert_ctx_accesses() -> bpf_patch_insn_data()
do_misc_fixups() -> bpf_patch_insn_data()
opt_subreg_zext_lo32_rnd_hi32() -> bpf_patch_insn_data()
fixup_call_args() -> bpf_jit_blind_constants() <-- only fixup here
All of these call adjust_insn_aux_data() which does:
memmove(data + off + cnt - 1, data + off, ...)
memset(data + off, 0, ...)
This moves indirect_target from data[off] to data[off + cnt - 1].
Meanwhile, bpf_insn_array_adjust() preserves xlated_off == off:
if (insn_array->values[i].xlated_off <= off)
continue;
So after an earlier pass patches an indirect target instruction,
the insn_array still says the target is at off, but
insn_aux_data has indirect_target set at off + cnt - 1.
If an indirect jump target is also, say, a helper call that
do_misc_fixups() replaces with multiple instructions, would
bpf_insn_is_indirect_target() return the wrong result for that
instruction? It seems like the fixup might need to be in
adjust_insn_aux_data() itself rather than only in
bpf_jit_blind_constants().
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22803043949
On 3/8/2026 1:14 AM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
>> --- a/kernel/bpf/core.c
>> +++ b/kernel/bpf/core.c
>> @@ -1475,11 +1475,30 @@ int bpf_jit_blind_constants(struct bpf_verifier_env *env)
>> insn = prog->insnsi + i + insn_delta;
>> insn_cnt += insn_delta;
>> i += insn_delta;
>> +
>> + /* bpf_patch_insn_data() calls adjust_insn_aux_data() to adjust insn_aux_data. The
>> + * indirect_target flag for the original instruction is moved to the last of the new
>> + * instructions, but the indirect jump target is actually the first one, so move
>> + * it back.
>> + */
>> + if (env->insn_aux_data[i].indirect_target) {
>> + env->insn_aux_data[i].indirect_target = 0;
>> + env->insn_aux_data[i - insn_delta].indirect_target = 1;
>> + }
>
> This fixup handles the displacement caused by
> bpf_jit_blind_constants() itself, but adjust_insn_aux_data() has
> the same displacement behavior in every caller of
> bpf_patch_insn_data(). Several patching passes run before
> bpf_jit_blind_constants() in bpf_check():
>
> convert_ctx_accesses() -> bpf_patch_insn_data()
> do_misc_fixups() -> bpf_patch_insn_data()
> opt_subreg_zext_lo32_rnd_hi32() -> bpf_patch_insn_data()
> fixup_call_args() -> bpf_jit_blind_constants() <-- only fixup here
>
> All of these call adjust_insn_aux_data() which does:
>
> memmove(data + off + cnt - 1, data + off, ...)
> memset(data + off, 0, ...)
>
> This moves indirect_target from data[off] to data[off + cnt - 1].
> Meanwhile, bpf_insn_array_adjust() preserves xlated_off == off:
>
> if (insn_array->values[i].xlated_off <= off)
> continue;
>
> So after an earlier pass patches an indirect target instruction,
> the insn_array still says the target is at off, but
> insn_aux_data has indirect_target set at off + cnt - 1.
>
> If an indirect jump target is also, say, a helper call that
> do_misc_fixups() replaces with multiple instructions, would
> bpf_insn_is_indirect_target() return the wrong result for that
> instruction? It seems like the fixup might need to be in
> adjust_insn_aux_data() itself rather than only in
> bpf_jit_blind_constants().
>
OK, will move the fixup to adjust_insn_aux_data()
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
>
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/22803043949
© 2016 - 2026 Red Hat, Inc.