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.
This keeps verifier-approved BTF accesses within the bytes provided by the
allocation kfunc.

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]>
---
 kernel/bpf/btf.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15ae7c43f..3e68af9c1 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7069,7 +7069,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 is_alloc)
 {
        u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;
        const struct btf_type *mtype, *elem_type = NULL;
@@ -7096,11 +7096,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 (is_alloc)
+                       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;
 
@@ -7363,7 +7366,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:
@@ -7441,7 +7445,7 @@ 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, false);
        if (err != WALK_STRUCT)
                return false;
 
-- 
2.34.1


Reply via email to