The R0:R2 return convention is derived from the BTF function prototype: bpf_compute_subprog_ret_regs() inspects the return type of every subprogram and records whether its value comes back in a register pair. 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. From that point on the verifier falls back to conservative, R0-only, semantics for the subprogram, while the compiled code keeps returning a pair and leaves the upper half in R2 behind the verifier's back. Rather than silently mistracking R2, 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 at the two places that can observe the flag: check_func_call(), for the call itself, and prepare_func_exit(), for the return from an inlined static subprogram. Note that the main program needs no such check: a >8 byte return from subprog 0 is rejected at BPF_EXIT regardless of whether its BTF is reliable. Callbacks need none either: a callback address only becomes a PTR_TO_FUNC through check_ld_imm(), which already rejects any callback returning more than 8 bytes. Signed-off-by: Yonghong Song --- kernel/bpf/verifier.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 41c47bcc3b0a..a01c8ecd9073 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -438,6 +438,21 @@ static void bpf_compute_subprog_ret_regs(struct bpf_verifier_env *env) } } +/* + * A >8 byte BPF return changes the calling convention to R0:R2, so the + * verifier can only allow it while the subprogram's prototype remains + * reliable. Once BTF is marked unreliable, reject the feature instead of + * silently falling back to R0-only semantics. + */ +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; @@ -9459,6 +9474,11 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, err = btf_check_subprog_call(env, subprog, caller->regs); if (err == -EFAULT) return err; + 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; + } if (bpf_subprog_is_global(env, subprog)) { const char *sub_name = subprog_name(env, subprog); @@ -9832,6 +9852,11 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) callee = state->frame[state->curframe]; r0 = &callee->regs[BPF_REG_0]; + if (subprog_ret_pair_unreliable(env, callee->subprogno)) { + verbose(env, "Func#%d ('%s') returns >8 bytes, which requires reliable BTF\n", + callee->subprogno, subprog_name(env, callee->subprogno)); + return -EINVAL; + } nregs = bpf_ret_reg_pair(env, callee->subprogno) ? 2 : 1; if (nregs > 1) env->prog->jit_required = 1; -- 2.53.0-Meta