A pointer member of a kernel struct that the verifier derives as trusted or RCU-protected is assumed to be non-NULL, so a load through it is a plain dereference. The BTF_TYPE_SAFE_*_OR_NULL lists exist for members known to be NULL at times, but they live in the verifier and have to be extended by hand. Teach btf_struct_walk() to report the "nullable" BTF type tag on a pointer member as PTR_MAYBE_NULL. The tag combines with any other tag on the member; it only says that the pointer may be NULL. The verifier keeps the flag whatever the pointer the member is read through, so a program has to check such a pointer before dereferencing it or passing it on, as the tag says. A tagged member of an existing struct thus asks every program that reads it for a check, including one that reads it through a probed pointer and relied on a NULL dereference reading as zero. The next patch uses the tag on __arena members to decide whether the injected address translation has to preserve NULL. Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/btf.c | 21 ++++++++++++++++++--- kernel/bpf/verifier.c | 6 ++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 64c78434fa05..3c01618694a4 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3557,7 +3557,8 @@ static int btf_type_tag_walk(const struct btf *btf, return 0; } -bool btf_type_is_arena_ptr(const struct btf *btf, const struct btf_type *t) +/* Whether the pointee of pointer type @t carries the type tag @name */ +static bool btf_ptr_has_type_tag(const struct btf *btf, const struct btf_type *t, const char *name) { if (!btf_type_is_ptr(t)) return false; @@ -3566,13 +3567,18 @@ bool btf_type_is_arena_ptr(const struct btf *btf, const struct btf_type *t) t = btf_type_by_id(btf, t->type)) { if (!btf_type_is_type_tag(t) || btf_type_kflag(t)) continue; - if (!strcmp(__btf_name_by_offset(btf, t->name_off), "arena")) + if (!strcmp(__btf_name_by_offset(btf, t->name_off), name)) return true; } return false; } +bool btf_type_is_arena_ptr(const struct btf *btf, const struct btf_type *t) +{ + return btf_ptr_has_type_tag(btf, t, "arena"); +} + static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, u32 off, int sz, struct btf_field_info *info, u32 field_mask) { @@ -7961,6 +7967,14 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, id = ctx.id; stype = ctx.t; + /* + * __nullable combines with any of the above: it only says + * that the pointer may be NULL, and every result of the + * walk keeps that. + */ + if (btf_ptr_has_type_tag(btf, mtype, "nullable")) + tmp_flag |= PTR_MAYBE_NULL; + if (btf_type_is_struct(stype)) { *next_btf_id = id; *flag |= tmp_flag; @@ -7969,6 +7983,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, return WALK_PTR; } + *flag |= tmp_flag & PTR_MAYBE_NULL; return WALK_PTR_UNTRUSTED; } @@ -8060,7 +8075,7 @@ int btf_struct_access(struct bpf_verifier_log *log, ret = PTR_TO_BTF_ID; goto check_variable_offset; case WALK_PTR_UNTRUSTED: - *flag = MEM_RDONLY | PTR_UNTRUSTED; + *flag = MEM_RDONLY | PTR_UNTRUSTED | (tmp_flag & PTR_MAYBE_NULL); ret = PTR_TO_MEM; goto check_variable_offset; case WALK_SCALAR: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f143b1b18b5c..b4d9ad3ca966 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2497,6 +2497,8 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env, mark_reg_known_zero(env, regs, regno); regs[regno].type = PTR_TO_MEM | flag; regs[regno].mem_size = 0; + if (type_may_be_null(flag)) + regs[regno].id = ++env->id_gen; return 0; default: verifier_bug(env, "unexpected reg_type %d in %s\n", reg_type, __func__); @@ -6712,9 +6714,9 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, * aggressively mark as untrusted otherwise such * pointers will be plain PTR_TO_BTF_ID without flags * and will be allowed to be passed into helpers for - * compat reasons. + * compat reasons. A __nullable member stays nullable. */ - flag = PTR_UNTRUSTED; + flag = PTR_UNTRUSTED | (flag & PTR_MAYBE_NULL); } } else { /* Old compat. Deprecated */ -- 2.53.0-Meta