BTF struct walks relax the struct-size check for accesses through a
trailing flexible array. That is valid for ordinary BTF type walking, but
PTR_TO_BTF_ID | MEM_ALLOC values point to objects allocated with the static
BTF type size.

When walking a MEM_ALLOC object, reject the access before applying the
flexible-array relaxation if the access range extends past the struct size.
Apply the same policy to struct ID matching so kfunc and kptr type checks
do not walk past the allocated object bounds either.

Fixes: 958cf2e273f0 ("bpf: Introduce bpf_obj_new")
Fixes: 36d8bdf75a93 ("bpf: Add alloc/xchg/direct_access support for local 
percpu kptr")
Signed-off-by: Yiyang Chen <[email protected]>
---
 include/linux/bpf.h   |  2 +-
 kernel/bpf/btf.c      | 17 +++++++++++------
 kernel/bpf/verifier.c | 11 +++++++----
 3 files changed, 19 insertions(+), 11 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 7719f6528..314d606dc 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3146,7 +3146,7 @@ int btf_struct_access(struct bpf_verifier_log *log,
 bool btf_struct_ids_match(struct bpf_verifier_log *log,
                          const struct btf *btf, u32 id, int off,
                          const struct btf *need_btf, u32 need_type_id,
-                         bool strict);
+                         bool strict, bool walk_flex_arrays);
 
 int btf_distill_func_proto(struct bpf_verifier_log *log,
                           struct btf *btf,
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 64572f85e..dff5c0d91 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7108,7 +7108,7 @@ enum bpf_struct_walk_result {
 static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,
                           const struct btf_type *t, int off, int size,
                           u32 *next_btf_id, enum bpf_type_flag *flag,
-                          const char **field_name)
+                          const char **field_name, bool walk_flex_arrays)
 {
        u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
        const struct btf_type *mtype, *elem_type = NULL;
@@ -7135,11 +7135,14 @@ static int btf_struct_walk(struct bpf_verifier_log 
*log, const struct btf *btf,
                *flag |= PTR_UNTRUSTED;
 
        if (off + size > t->size) {
+               struct btf_array *array_elem;
+
+               if (!walk_flex_arrays)
+                       goto error;
+
                /* If the last element is a variable size array, we may
                 * need to relax the rule.
                 */
-               struct btf_array *array_elem;
-
                if (vlen == 0)
                        goto error;
 
@@ -7404,7 +7407,8 @@ int btf_struct_access(struct bpf_verifier_log *log,
 
        t = btf_type_by_id(btf, id);
        do {
-               err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, 
field_name);
+               err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag,
+                                     field_name, !type_is_alloc(reg->type));
 
                switch (err) {
                case WALK_PTR:
@@ -7463,7 +7467,7 @@ bool btf_types_are_same(const struct btf *btf1, u32 id1,
 bool btf_struct_ids_match(struct bpf_verifier_log *log,
                          const struct btf *btf, u32 id, int off,
                          const struct btf *need_btf, u32 need_type_id,
-                         bool strict)
+                         bool strict, bool walk_flex_arrays)
 {
        const struct btf_type *type;
        enum bpf_type_flag flag = 0;
@@ -7482,7 +7486,8 @@ bool btf_struct_ids_match(struct bpf_verifier_log *log,
        type = btf_type_by_id(btf, id);
        if (!type)
                return false;
-       err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);
+       err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL,
+                             walk_flex_arrays);
        if (err != WALK_STRUCT)
                return false;
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 25aea4271..4d3bb7156 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -4379,7 +4379,8 @@ static int map_kptr_match_type(struct bpf_verifier_env 
*env,
         */
        if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 
reg->var_off.value,
                                  kptr_field->kptr.btf, kptr_field->kptr.btf_id,
-                                 kptr_field->type != BPF_KPTR_UNREF))
+                                 kptr_field->type != BPF_KPTR_UNREF,
+                                 !type_is_alloc(reg->type)))
                goto bad_type;
        return 0;
 bad_type:
@@ -7970,7 +7971,7 @@ static int check_reg_type(struct bpf_verifier_env *env, 
struct bpf_reg_state *re
 
                        if (!btf_struct_ids_match(&env->log, reg->btf, 
reg->btf_id,
                                                  reg->var_off.value, 
btf_vmlinux, *arg_btf_id,
-                                                 strict_type_match)) {
+                                                 strict_type_match, 
!type_is_alloc(reg->type))) {
                                verbose(env, "%s is of type %s but %s is 
expected\n",
                                        reg_arg_name(env, argno),
                                        btf_type_name(reg->btf, reg->btf_id),
@@ -11429,7 +11430,8 @@ static int process_kf_arg_ptr_to_btf_id(struct 
bpf_verifier_env *env,
        reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, &reg_ref_id);
        reg_ref_tname = btf_name_by_offset(reg_btf, reg_ref_t->name_off);
        struct_same = btf_struct_ids_match(&env->log, reg_btf, reg_ref_id, 
reg->var_off.value,
-                                          meta->btf, ref_id, 
strict_type_match);
+                                          meta->btf, ref_id, strict_type_match,
+                                          !type_is_alloc(reg->type));
        /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot
         * actually use it -- it must cast to the underlying type. So we allow
         * caller to pass in the underlying type.
@@ -11876,7 +11878,8 @@ __process_kf_arg_ptr_to_graph_node(struct 
bpf_verifier_env *env,
        et = btf_type_by_id(field->graph_root.btf, 
field->graph_root.value_btf_id);
        t = btf_type_by_id(reg->btf, reg->btf_id);
        if (!btf_struct_ids_match(&env->log, reg->btf, reg->btf_id, 0, 
field->graph_root.btf,
-                                 field->graph_root.value_btf_id, true)) {
+                                 field->graph_root.value_btf_id, true,
+                                 !type_is_alloc(reg->type))) {
                verbose(env, "operation on %s expects arg#1 %s at offset=%d "
                        "in struct %s, but arg is at offset=%d in struct %s\n",
                        btf_field_type_name(head_field_type),
-- 
2.34.1


Reply via email to