Since kfuncs don't have a prototype defined, the BTF code has to infer the information in a different way. For example, naming a parameter with the __sz suffix is currently the way to declare such parameter as a memory size. Another feature not available without a prototype was flagging an argument as PTR_MAYBE_NULL, meaning that the function is trusted to correctly handle a NULL pointer. Similarly to the __sz suffix, introduce the __maybe_null suffix to have an equivalent feature of setting the PTR_MAYBE_NULL flag. Cc: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx> Cc: Kumar Kartikeya Dwivedi <memxor@xxxxxxxxx> Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx> --- kernel/bpf/btf.c | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 9e94571d1626..80e3098fcb48 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6142,18 +6142,13 @@ static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log, return true; } -static bool is_kfunc_arg_mem_size(const struct btf *btf, - const struct btf_param *arg, - const struct bpf_reg_state *reg) +static bool btf_param_match_suffix(const struct btf *btf, + const struct btf_param *arg, + const char *suffix) { - int len, sfx_len = sizeof("__sz") - 1; - const struct btf_type *t; + int len, sfx_len = strlen(suffix); const char *param_name; - t = btf_type_skip_modifiers(btf, arg->type, NULL); - if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) - return false; - /* In the future, this can be ported to use BTF tagging */ param_name = btf_name_by_offset(btf, arg->name_off); if (str_is_empty(param_name)) @@ -6162,12 +6157,25 @@ static bool is_kfunc_arg_mem_size(const struct btf *btf, if (len < sfx_len) return false; param_name += len - sfx_len; - if (strncmp(param_name, "__sz", sfx_len)) + if (strncmp(param_name, suffix, sfx_len)) return false; return true; } +static bool is_kfunc_arg_mem_size(const struct btf *btf, + const struct btf_param *arg, + const struct bpf_reg_state *reg) +{ + const struct btf_type *t; + + t = btf_type_skip_modifiers(btf, arg->type, NULL); + if (!btf_type_is_scalar(t) || reg->type != SCALAR_VALUE) + return false; + + return btf_param_match_suffix(btf, arg, "__sz"); +} + static int btf_check_func_arg_match(struct bpf_verifier_env *env, const struct btf *btf, u32 func_id, struct bpf_reg_state *regs, @@ -6302,8 +6310,12 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, i, btf_type_str(t)); return -EINVAL; } - } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || - (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) { + } else if (is_kfunc && ((reg->type == PTR_TO_BTF_ID || + (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type))) || + (btf_param_match_suffix(btf, &args[i], "__maybe_null") && + (reg->type == PTR_TO_BTF_ID_OR_NULL || + (reg2btf_ids[base_type(reg->type)] && + type_flag(reg->type) == PTR_MAYBE_NULL))))) { const struct btf_type *reg_ref_t; const struct btf *reg_btf; const char *reg_ref_tname; @@ -6316,7 +6328,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } - if (reg->type == PTR_TO_BTF_ID) { + if (base_type(reg->type) == PTR_TO_BTF_ID) { reg_btf = reg->btf; reg_ref_id = reg->btf_id; /* Ensure only one argument is referenced PTR_TO_BTF_ID */ @@ -6354,6 +6366,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, if (is_kfunc) { bool arg_mem_size = i + 1 < nargs && is_kfunc_arg_mem_size(btf, &args[i + 1], ®s[regno + 1]); bool arg_dynptr = !strcmp(ref_tname, "bpf_dynptr_kern"); + bool arg_null_ok = btf_param_match_suffix(btf, &args[i], + "__maybe_null") && + reg->type == SCALAR_VALUE && + tnum_equals_const(reg->var_off, 0); /* Permit pointer to mem, but only when argument * type is pointer to scalar, or struct composed @@ -6363,7 +6379,7 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, */ if (!btf_type_is_scalar(ref_t) && !__btf_type_is_scalar_struct(log, btf, ref_t, 0) && - !arg_dynptr && + !arg_dynptr && !arg_null_ok && (arg_mem_size ? !btf_type_is_void(ref_t) : 1)) { bpf_log(log, "arg#%d pointer type %s %s must point to %sscalar, or struct with scalar\n", @@ -6371,6 +6387,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, return -EINVAL; } + /* kfunc accepts NULL pointer. */ + if (arg_null_ok) + continue; + /* Assume initialized dynptr. */ if (arg_dynptr) { if (!is_dynptr_reg_valid_init(env, reg, -- 2.25.1