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.