Re: [PATCH bpf-next v4 2/4] bpf: Add helper to detect indirect jump targets
From: Xu Kuohai <xukuohai@huaweicloud.com>
Date: 2026-01-15 07:37:54
Also in:
bpf, lkml
On 1/14/2026 7:00 PM, Anton Protopopov wrote: [...]
quoted
+ +bool bpf_insn_is_indirect_target(const struct bpf_prog *prog, int idx) +{ + return prog->aux->insn_aux && prog->aux->insn_aux[idx].indirect_target;Is there a case when insn_aux is NULL?
It is NULL when there is no indirect jump targets for the bpf prog, see the has_indirect_target test in clone_insn_aux_data.
quoted
+} #endif /* CONFIG_BPF_JIT */ /* Base function for offset calculation. Needs to go into .text section,@@ -2540,24 +2579,24 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) if (!bpf_prog_is_offloaded(fp->aux)) { *err = bpf_prog_alloc_jited_linfo(fp); if (*err) - return fp; + goto free_insn_aux; fp = bpf_int_jit_compile(fp); bpf_prog_jit_attempt_done(fp); if (!fp->jited && jit_needed) { *err = -ENOTSUPP; - return fp; + goto free_insn_aux; } } else { *err = bpf_prog_offload_compile(fp); if (*err) - return fp; + goto free_insn_aux; } finalize: *err = bpf_prog_lock_ro(fp); if (*err) - return fp; + goto free_insn_aux; /* The tail call compatibility check can only be done at * this late stage as we need to determine, if we deal@@ -2566,6 +2605,10 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) */ *err = bpf_check_tail_call(fp); +free_insn_aux: + vfree(fp->aux->insn_aux); + fp->aux->insn_aux = NULL; + return fp; } EXPORT_SYMBOL_GPL(bpf_prog_select_runtime);diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 22605d9e0ffa..f2fe6baeceb9 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c@@ -3852,6 +3852,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)@@ -20337,6 +20342,7 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in } for (i = 0; i < n; i++) {^ n -> n-1
ACK
quoted
+ 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))@@ -21243,6 +21249,37 @@ static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env) }mark_indirect_target(n-1)quoted
} +static int clone_insn_aux_data(struct bpf_prog *prog, struct bpf_verifier_env *env, u32 off) +{ + u32 i; + size_t size; + bool has_indirect_target = false; + struct bpf_insn_aux_data *insn_aux; + + for (i = 0; i < prog->len; i++) { + if (env->insn_aux_data[off + i].indirect_target) { + has_indirect_target = true; + break; + } + } + + /* insn_aux is copied into bpf_prog so the JIT can check whether an instruction is an + * indirect jump target. If no indirect jump targets exist, copying is unnecessary. + */ + if (!has_indirect_target) + return 0; + + size = array_size(sizeof(struct bpf_insn_aux_data), prog->len); + insn_aux = vzalloc(size); + if (!insn_aux) + return -ENOMEM; + + memcpy(insn_aux, env->insn_aux_data + off, size); + prog->aux->insn_aux = insn_aux; + + return 0; +} + /* single env->prog->insni[off] instruction was replaced with the range * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying * [0, off) and [off, end) to new locations, so the patched range stays zero@@ -22239,6 +22276,10 @@ static int jit_subprogs(struct bpf_verifier_env *env) if (!i) func[i]->aux->exception_boundary = env->seen_exception; + err = clone_insn_aux_data(func[i], env, subprog_start); + if (err < 0) + goto out_free; + /* * To properly pass the absolute subprog start to jit * all instruction adjustments should be accumulated@@ -22306,6 +22347,8 @@ static int jit_subprogs(struct bpf_verifier_env *env) for (i = 0; i < env->subprog_cnt; i++) { func[i]->aux->used_maps = NULL; func[i]->aux->used_map_cnt = 0; + vfree(func[i]->aux->insn_aux); + func[i]->aux->insn_aux = NULL; } /* finally lock prog and jit images for all functions and@@ -22367,6 +22410,7 @@ static int jit_subprogs(struct bpf_verifier_env *env) for (i = 0; i < env->subprog_cnt; i++) { if (!func[i]) continue; + vfree(func[i]->aux->insn_aux); func[i]->aux->poke_tab = NULL; bpf_jit_free(func[i]); }@@ -25350,6 +25394,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3 env->verification_time = ktime_get_ns() - start_time; print_verification_stats(env); env->prog->aux->verified_insns = env->insn_processed; + env->prog->aux->insn_aux = env->insn_aux_data; /* preserve original error even if log finalization is successful */ err = bpf_vlog_finalize(&env->log, &log_true_size);@@ -25428,7 +25473,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, __u3 if (!is_priv) mutex_unlock(&bpf_verifier_lock); clear_insn_aux_data(env, 0, env->prog->len); - vfree(env->insn_aux_data); + /* on success, insn_aux_data will be freed by bpf_prog_select_runtime */ + if (ret) { + vfree(env->insn_aux_data); + env->prog->aux->insn_aux = NULL; + } err_free_env: bpf_stack_liveness_free(env); kvfree(env->cfg.insn_postorder);-- 2.47.3LGTM, just in case, could you please tell how you have tested this patchset exactly?
I ran test_progs-cpuv4 on machines supporting x86 CET/IBT and arm64 BTI. I tested in three environments: an arm64 physical machine with BTI support (CPU: Hisilicon KP920B), an arm64 QEMU VM using cpu=max for BTI support, and a Bochs VM with model=arrow_lake for x86 CET/IBT support.