[PATCH bpf-next v5 08/11] bpf: Reject register-pair returns when the subprog BTF is unreliable

Yonghong Song <[email protected]>
Newsgroups org.kernel.vger.bpf
Message-ID <[email protected]>
btf_check_subprog_call() can decide, at a call site, that this BTF is not
to be trusted and mark the subprogram unreliable, which happens when
compiler optimizations remove arguments from a static function or when a
mismatched type is passed to a global one. Verification carries on, but
the prototype the return convention was read from is one the verifier has
already declared not to describe the compiled code.

Rather than keep tracking R2 on the strength of a discarded signature,
reject a return value larger than 8 bytes as soon as the prototype it was
derived from becomes unreliable. Add subprog_ret_pair_unreliable() and
test it in check_func_call() on the path to a static subprogram, which is
where the flag can be observed while the call still proceeds. For a static
callee this only triggers on a genuine argument mismatch, since the
previous patch stopped btf_validate_return_type() from marking a local
function unreliable for returning an aggregate.

No check is needed anywhere else:
 - a global subprogram is already rejected by the existing "Caller passes
   invalid args into func#N" path, because btf_check_subprog_call()
   returns an error both when it marks the BTF unreliable and on every
   later call;
 - the main program does not use the convention at all: its return value
   is the program's exit code, read out of R0, so nothing looks at R2
   there;

Signed-off-by: Yonghong Song <[email protected]>
---
 kernel/bpf/verifier.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b23be0fa95af..0ffb3bed1649 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -441,6 +441,23 @@ static void bpf_compute_subprog_ret_regs(struct bpf_verifier_env *env)
 	}
 }
 
+/*
+ * A >8 byte BPF return changes the calling convention to R0:R2, and the
+ * verifier derives that convention from the subprogram's BTF prototype
+ * alone. Once that prototype is marked unreliable it is known not to
+ * describe the compiled code, so the convention read from it cannot be
+ * trusted either: reject the call rather than keep tracking R2 on the
+ * strength of a signature the verifier has already discarded.
+ */
+static bool subprog_ret_pair_unreliable(struct bpf_verifier_env *env, int subprog)
+{
+	struct bpf_prog_aux *aux = env->prog->aux;
+
+	return bpf_ret_reg_pair(env, subprog) &&
+	       aux->func_info_aux &&
+	       aux->func_info_aux[subprog].unreliable;
+}
+
 static const char *subprog_name(const struct bpf_verifier_env *env, int subprog)
 {
 	struct bpf_func_info *info;
@@ -9527,6 +9544,12 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		return 0;
 	}
 
+	if (subprog_ret_pair_unreliable(env, subprog)) {
+		verbose(env, "Func#%d ('%s') returns >8 bytes, which requires reliable BTF\n",
+			subprog, subprog_name(env, subprog));
+		return -EINVAL;
+	}
+
 	/*
 	 * Track caller's total stack arg count (incoming + max outgoing).
 	 * This is needed so the JIT knows how much stack arg space to allocate.
-- 
2.53.0-Meta
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.