Global subprogs are currently not allowed to return void. Adjust verifier logic to allow global functions with a void return type. Also adjust existing selftests that were ensuring that global subprogs with void return types fail to verify. These tests should now load successfully. Cc: Kumar Kartikeya Dwivedi Signed-off-by: Emil Tsalapatis --- kernel/bpf/btf.c | 7 +++-- kernel/bpf/verifier.c | 29 ++++++++++++++++++- .../selftests/bpf/progs/exceptions_fail.c | 2 +- .../selftests/bpf/progs/test_global_func7.c | 2 +- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 8959f3bc1e92..162bfb948ba1 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -7835,15 +7835,16 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog) tname, nargs, MAX_BPF_FUNC_REG_ARGS); return -EINVAL; } - /* check that function returns int, exception cb also requires this */ + /* check that function is void or returns int, exception cb also requires this */ t = btf_type_by_id(btf, t->type); while (btf_type_is_modifier(t)) t = btf_type_by_id(btf, t->type); - if (!btf_type_is_int(t) && !btf_is_any_enum(t)) { + if (!btf_type_is_void(t) && !btf_type_is_int(t) && !btf_is_any_enum(t)) { if (!is_global) return -EINVAL; bpf_log(log, - "Global function %s() doesn't return scalar. Only those are supported.\n", + "Global function %s() return value not void or scalar. " + "Only those are supported.\n", tname); return -EINVAL; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c2f2650db9fd..2cc740495479 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -443,6 +443,29 @@ static bool subprog_is_global(const struct bpf_verifier_env *env, int subprog) return aux && aux[subprog].linkage == BTF_FUNC_GLOBAL; } +static bool subprog_is_void(struct bpf_verifier_env *env, int subprog) +{ + const struct btf_type *type, *func, *func_proto; + const struct btf *btf = env->prog->aux->btf; + u32 btf_id; + + btf_id = env->prog->aux->func_info[subprog].type_id; + + func = btf_type_by_id(btf, btf_id); + if (verifier_bug_if(!func, env, "btf_id %u not found", btf_id)) + return false; + + func_proto = btf_type_by_id(btf, func->type); + if (verifier_bug_if(!func_proto, env, "btf_id %u not found", func->type)) + return false; + + type = btf_type_skip_modifiers(btf, func_proto->type, NULL); + if (verifier_bug_if(!type, env, "btf_id %u not found", func_proto->type)) + return false; + + return btf_type_is_void(type); +} + static const char *subprog_name(const struct bpf_verifier_env *env, int subprog) { struct bpf_func_info *info; @@ -10875,7 +10898,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn, subprog_aux(env, subprog)->called = true; clear_caller_saved_regs(env, caller->regs); - /* All global functions return a 64-bit SCALAR_VALUE */ + /* All non-void global functions return a 64-bit SCALAR_VALUE. */ mark_reg_unknown(env, caller->regs, BPF_REG_0); caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG; @@ -17786,6 +17809,10 @@ static int check_return_code(struct bpf_verifier_env *env, int regno, const char default: break; } + } else { + /* If this is a void global subprog, there is no return value. */ + if (subprog_is_global(env, frame->subprogno) && subprog_is_void(env, frame->subprogno)) + return 0; } /* eBPF calling convention is such that R0 is used diff --git a/tools/testing/selftests/bpf/progs/exceptions_fail.c b/tools/testing/selftests/bpf/progs/exceptions_fail.c index 8a0fdff89927..d28ecc4ee2d0 100644 --- a/tools/testing/selftests/bpf/progs/exceptions_fail.c +++ b/tools/testing/selftests/bpf/progs/exceptions_fail.c @@ -51,7 +51,7 @@ __noinline int exception_cb_ok_arg_small(int a) SEC("?tc") __exception_cb(exception_cb_bad_ret_type) -__failure __msg("Global function exception_cb_bad_ret_type() doesn't return scalar.") +__failure __msg("Global function exception_cb_bad_ret_type() return value not void or scalar.") int reject_exception_cb_type_1(struct __sk_buff *ctx) { bpf_throw(0); diff --git a/tools/testing/selftests/bpf/progs/test_global_func7.c b/tools/testing/selftests/bpf/progs/test_global_func7.c index f182febfde3c..9e59625c1c92 100644 --- a/tools/testing/selftests/bpf/progs/test_global_func7.c +++ b/tools/testing/selftests/bpf/progs/test_global_func7.c @@ -12,7 +12,7 @@ void foo(struct __sk_buff *skb) } SEC("tc") -__failure __msg("foo() doesn't return scalar") +__success int global_func7(struct __sk_buff *skb) { foo(skb); -- 2.49.0