bpf: fix callx backtracking Signed-off-by: Alexei Starovoitov <ast@kernel.org>
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c index 854731d..a652ca8 100644 --- a/kernel/bpf/backtrack.c +++ b/kernel/bpf/backtrack.c
@@ -374,9 +374,14 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, if (class == BPF_STX) bt_set_reg(bt, sreg); } else if (class == BPF_JMP || class == BPF_JMP32) { - if (bpf_pseudo_call(insn)) { + if (bpf_pseudo_call(insn) || + insn->code == (BPF_JMP | BPF_CALL | BPF_X)) { int subprog_insn_idx, subprog; + /* callx is the same as subprog call */ + if (insn->code == (BPF_JMP | BPF_CALL | BPF_X)) + goto static_subprog_call; + subprog_insn_idx = idx + insn->imm + 1; subprog = bpf_find_subprog(env, subprog_insn_idx); if (subprog < 0) @@ -404,6 +409,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx, bt_clear_reg(bt, BPF_REG_0); return 0; } else { +static_subprog_call: /* static subprog call instruction, which * means that we are exiting current subprog, * so only r1-r5 could be still requested as
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index 2337fbe..5d3eada 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c
@@ -2098,6 +2098,8 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env, case BPF_CALL: def = ALL_CALLER_SAVED_REGS; use = def & ~BIT(BPF_REG_0); + if (BPF_SRC(insn->code) == BPF_X) + use |= dst; /* callx reads dst_reg as fn ptr */ if (bpf_get_call_summary(env, insn, &cs)) use = GENMASK(cs.num_params, 1); break;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index b5eadb7..c91f1f8 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c
@@ -9600,6 +9600,11 @@ static int do_check_func_call(struct bpf_verifier_env *env, int subprog, int *in /* and go analyze first insn of the callee */ *insn_idx = env->subprog_info[subprog].start - 1; + /* For callx: mark callee entry as jmp_point so the backtracking + * jmp_history records the jump from caller to callee. + */ + mark_jmp_point(env, env->subprog_info[subprog].start); + if (env->log.level & BPF_LOG_LEVEL) { verbose(env, "caller:\n"); print_verifier_state(env, state, caller->frameno, true); @@ -9624,7 +9629,8 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, return do_check_func_call(env, subprog, insn_idx); } -/* callx *dst_reg: indirect call through a register that holds a function +/* + * callx *dst_reg: indirect call through a register that holds a function * pointer previously loaded via ldimm64 BPF_PSEUDO_FUNC. The target subprog * is identified by reg->subprogno, so once that has been verified the call * is processed identically to a direct BPF_PSEUDO_CALL to the same subprog.