bpf: Make btf_find_field more generic
authorKumar Kartikeya Dwivedi <memxor@gmail.com>
Fri, 15 Apr 2022 16:03:42 +0000 (21:33 +0530)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 21 Apr 2022 14:31:10 +0000 (16:31 +0200)
Next commit introduces field type 'kptr' whose kind will not be struct,
but pointer, and it will not be limited to one offset, but multiple
ones. Make existing btf_find_struct_field and btf_find_datasec_var
functions amenable to use for finding kptrs in map value, by moving
spin_lock and timer specific checks into their own function.

The alignment, and name are checked before the function is called, so it
is the last point where we can skip field or return an error before the
next loop iteration happens. Size of the field and type is meant to be
checked inside the function.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20220415160354.1050687-2-memxor@gmail.com
kernel/bpf/btf.c

index 7906b9bf7ff8f40c706e755ad9c7278f077ae8b5..0493310d981ff6166280b3bc85c0aa6a240317d6 100644 (file)
@@ -3163,24 +3163,44 @@ static void btf_struct_log(struct btf_verifier_env *env,
        btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
 }
 
+enum btf_field_type {
+       BTF_FIELD_SPIN_LOCK,
+       BTF_FIELD_TIMER,
+};
+
+struct btf_field_info {
+       u32 off;
+};
+
+static int btf_find_struct(const struct btf *btf, const struct btf_type *t,
+                          u32 off, int sz, struct btf_field_info *info)
+{
+       if (!__btf_type_is_struct(t))
+               return 0;
+       if (t->size != sz)
+               return 0;
+       if (info->off != -ENOENT)
+               /* only one such field is allowed */
+               return -E2BIG;
+       info->off = off;
+       return 0;
+}
+
 static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t,
-                                const char *name, int sz, int align)
+                                const char *name, int sz, int align,
+                                enum btf_field_type field_type,
+                                struct btf_field_info *info)
 {
        const struct btf_member *member;
-       u32 i, off = -ENOENT;
+       u32 i, off;
 
        for_each_member(i, t, member) {
                const struct btf_type *member_type = btf_type_by_id(btf,
                                                                    member->type);
-               if (!__btf_type_is_struct(member_type))
-                       continue;
-               if (member_type->size != sz)
-                       continue;
+
                if (strcmp(__btf_name_by_offset(btf, member_type->name_off), name))
                        continue;
-               if (off != -ENOENT)
-                       /* only one such field is allowed */
-                       return -E2BIG;
+
                off = __btf_member_bit_offset(t, member);
                if (off % 8)
                        /* valid C code cannot generate such BTF */
@@ -3188,46 +3208,76 @@ static int btf_find_struct_field(const struct btf *btf, const struct btf_type *t
                off /= 8;
                if (off % align)
                        return -EINVAL;
+
+               switch (field_type) {
+               case BTF_FIELD_SPIN_LOCK:
+               case BTF_FIELD_TIMER:
+                       return btf_find_struct(btf, member_type, off, sz, info);
+               default:
+                       return -EFAULT;
+               }
        }
-       return off;
+       return 0;
 }
 
 static int btf_find_datasec_var(const struct btf *btf, const struct btf_type *t,
-                               const char *name, int sz, int align)
+                               const char *name, int sz, int align,
+                               enum btf_field_type field_type,
+                               struct btf_field_info *info)
 {
        const struct btf_var_secinfo *vsi;
-       u32 i, off = -ENOENT;
+       u32 i, off;
 
        for_each_vsi(i, t, vsi) {
                const struct btf_type *var = btf_type_by_id(btf, vsi->type);
                const struct btf_type *var_type = btf_type_by_id(btf, var->type);
 
-               if (!__btf_type_is_struct(var_type))
-                       continue;
-               if (var_type->size != sz)
+               off = vsi->offset;
+
+               if (strcmp(__btf_name_by_offset(btf, var_type->name_off), name))
                        continue;
                if (vsi->size != sz)
                        continue;
-               if (strcmp(__btf_name_by_offset(btf, var_type->name_off), name))
-                       continue;
-               if (off != -ENOENT)
-                       /* only one such field is allowed */
-                       return -E2BIG;
-               off = vsi->offset;
                if (off % align)
                        return -EINVAL;
+
+               switch (field_type) {
+               case BTF_FIELD_SPIN_LOCK:
+               case BTF_FIELD_TIMER:
+                       return btf_find_struct(btf, var_type, off, sz, info);
+               default:
+                       return -EFAULT;
+               }
        }
-       return off;
+       return 0;
 }
 
 static int btf_find_field(const struct btf *btf, const struct btf_type *t,
-                         const char *name, int sz, int align)
+                         enum btf_field_type field_type,
+                         struct btf_field_info *info)
 {
+       const char *name;
+       int sz, align;
+
+       switch (field_type) {
+       case BTF_FIELD_SPIN_LOCK:
+               name = "bpf_spin_lock";
+               sz = sizeof(struct bpf_spin_lock);
+               align = __alignof__(struct bpf_spin_lock);
+               break;
+       case BTF_FIELD_TIMER:
+               name = "bpf_timer";
+               sz = sizeof(struct bpf_timer);
+               align = __alignof__(struct bpf_timer);
+               break;
+       default:
+               return -EFAULT;
+       }
 
        if (__btf_type_is_struct(t))
-               return btf_find_struct_field(btf, t, name, sz, align);
+               return btf_find_struct_field(btf, t, name, sz, align, field_type, info);
        else if (btf_type_is_datasec(t))
-               return btf_find_datasec_var(btf, t, name, sz, align);
+               return btf_find_datasec_var(btf, t, name, sz, align, field_type, info);
        return -EINVAL;
 }
 
@@ -3237,16 +3287,24 @@ static int btf_find_field(const struct btf *btf, const struct btf_type *t,
  */
 int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t)
 {
-       return btf_find_field(btf, t, "bpf_spin_lock",
-                             sizeof(struct bpf_spin_lock),
-                             __alignof__(struct bpf_spin_lock));
+       struct btf_field_info info = { .off = -ENOENT };
+       int ret;
+
+       ret = btf_find_field(btf, t, BTF_FIELD_SPIN_LOCK, &info);
+       if (ret < 0)
+               return ret;
+       return info.off;
 }
 
 int btf_find_timer(const struct btf *btf, const struct btf_type *t)
 {
-       return btf_find_field(btf, t, "bpf_timer",
-                             sizeof(struct bpf_timer),
-                             __alignof__(struct bpf_timer));
+       struct btf_field_info info = { .off = -ENOENT };
+       int ret;
+
+       ret = btf_find_field(btf, t, BTF_FIELD_TIMER, &info);
+       if (ret < 0)
+               return ret;
+       return info.off;
 }
 
 static void __btf_struct_show(const struct btf *btf, const struct btf_type *t,