Let's ensure that the PTR_TO_BTF_ID reg being passed in to release kfunc always has its offset set to 0. While not a real problem now, there's a very real possibility this will become a problem when more and more kfuncs are exposed. Previous commits already protected against non-zero var_off. The case we are concerned about now is when we have a type that can be returned by acquire kfunc: struct foo { int a; int b; struct bar b; }; ... and struct bar is also a type that can be returned by another acquire kfunc. Then, doing the following sequence: struct foo *f = bpf_get_foo(); // acquire kfunc if (!f) return 0; bpf_put_bar(&f->b); // release kfunc ... would work with the current code, since the btf_struct_ids_match takes reg->off into account for matching pointer type with release kfunc argument type, but would obviously be incorrect, and most likely lead to a kernel crash. A test has been included later to prevent regressions in this area. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> --- kernel/bpf/btf.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 7f6a0ae5028b..ba6845225b65 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -5753,6 +5753,9 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } + if (is_kfunc) + rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog), + BTF_KFUNC_TYPE_RELEASE, func_id); /* check that BTF function arguments match actual types that the * verifier sees. */ @@ -5816,6 +5819,16 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, regno, reg->ref_obj_id, ref_obj_id); return -EFAULT; } + /* Ensure that offset of referenced PTR_TO_BTF_ID is + * always zero, when passed to release function. + * var_off has already been checked to be 0 by + * check_func_arg_reg_off. + */ + if (rel && reg->off) { + bpf_log(log, "R%d with ref_obj_id=%d must have zero offset when passed to release kfunc\n", + regno, reg->ref_obj_id); + return -EINVAL; + } ref_regno = regno; ref_obj_id = reg->ref_obj_id; } @@ -5892,8 +5905,6 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, /* Either both are set, or neither */ WARN_ON_ONCE((ref_obj_id && !ref_regno) || (!ref_obj_id && ref_regno)); if (is_kfunc) { - rel = btf_kfunc_id_set_contains(btf, resolve_prog_type(env->prog), - BTF_KFUNC_TYPE_RELEASE, func_id); /* We already made sure ref_obj_id is set only for one argument */ if (rel && !ref_obj_id) { bpf_log(log, "release kernel function %s expects refcounted PTR_TO_BTF_ID\n", -- 2.35.1