]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
bpf: btf: Introduce BPF Type Format (BTF)
authorMartin KaFai Lau <kafai@fb.com>
Wed, 18 Apr 2018 22:55:57 +0000 (15:55 -0700)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 19 Apr 2018 19:46:24 +0000 (21:46 +0200)
This patch introduces BPF type Format (BTF).

BTF (BPF Type Format) is the meta data format which describes
the data types of BPF program/map.  Hence, it basically focus
on the C programming language which the modern BPF is primary
using.  The first use case is to provide a generic pretty print
capability for a BPF map.

BTF has its root from CTF (Compact C-Type format).  To simplify
the handling of BTF data, BTF removes the differences between
small and big type/struct-member.  Hence, BTF consistently uses u32
instead of supporting both "one u16" and "two u32 (+padding)" in
describing type and struct-member.

It also raises the number of types (and functions) limit
from 0x7fff to 0x7fffffff.

Due to the above changes,  the format is not compatible to CTF.
Hence, BTF starts with a new BTF_MAGIC and version number.

This patch does the first verification pass to the BTF.  The first
pass checks:
1. meta-data size (e.g. It does not go beyond the total btf's size)
2. name_offset is valid
3. Each BTF_KIND (e.g. int, enum, struct....) does its
   own check of its meta-data.

Some other checks, like checking a struct's member is referring
to a valid type, can only be done in the second pass.  The second
verification pass will be implemented in the next patch.

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
include/uapi/linux/btf.h [new file with mode: 0644]
kernel/bpf/Makefile
kernel/bpf/btf.c [new file with mode: 0644]

diff --git a/include/uapi/linux/btf.h b/include/uapi/linux/btf.h
new file mode 100644 (file)
index 0000000..74a30b1
--- /dev/null
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2018 Facebook */
+#ifndef _UAPI__LINUX_BTF_H__
+#define _UAPI__LINUX_BTF_H__
+
+#include <linux/types.h>
+
+#define BTF_MAGIC      0xeB9F
+#define BTF_MAGIC_SWAP 0x9FeB
+#define BTF_VERSION    1
+#define BTF_FLAGS_COMPR        0x01
+
+struct btf_header {
+       __u16   magic;
+       __u8    version;
+       __u8    flags;
+
+       __u32   parent_label;
+       __u32   parent_name;
+
+       /* All offsets are in bytes relative to the end of this header */
+       __u32   label_off;      /* offset of label section      */
+       __u32   object_off;     /* offset of data object section*/
+       __u32   func_off;       /* offset of function section   */
+       __u32   type_off;       /* offset of type section       */
+       __u32   str_off;        /* offset of string section     */
+       __u32   str_len;        /* length of string section     */
+};
+
+/* Max # of type identifier */
+#define BTF_MAX_TYPE   0x7fffffff
+/* Max offset into the string section */
+#define BTF_MAX_NAME_OFFSET    0x7fffffff
+/* Max # of struct/union/enum members or func args */
+#define BTF_MAX_VLEN   0xffff
+
+/* The type id is referring to a parent BTF */
+#define BTF_TYPE_PARENT(id)    (((id) >> 31) & 0x1)
+#define BTF_TYPE_ID(id)                ((id) & BTF_MAX_TYPE)
+
+/* String is in the ELF string section */
+#define BTF_STR_TBL_ELF_ID(ref)        (((ref) >> 31) & 0x1)
+#define BTF_STR_OFFSET(ref)    ((ref) & BTF_MAX_NAME_OFFSET)
+
+struct btf_type {
+       __u32 name;
+       /* "info" bits arrangement
+        * bits  0-15: vlen (e.g. # of struct's members)
+        * bits 16-23: unused
+        * bits 24-28: kind (e.g. int, ptr, array...etc)
+        * bits 29-30: unused
+        * bits    31: root
+        */
+       __u32 info;
+       /* "size" is used by INT, ENUM, STRUCT and UNION.
+        * "size" tells the size of the type it is describing.
+        *
+        * "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
+        * "type" is a type_id referring to another type.
+        */
+       union {
+               __u32 size;
+               __u32 type;
+       };
+};
+
+#define BTF_INFO_KIND(info)    (((info) >> 24) & 0x1f)
+#define BTF_INFO_ISROOT(info)  (!!(((info) >> 24) & 0x80))
+#define BTF_INFO_VLEN(info)    ((info) & 0xffff)
+
+#define BTF_KIND_UNKN          0       /* Unknown      */
+#define BTF_KIND_INT           1       /* Integer      */
+#define BTF_KIND_PTR           2       /* Pointer      */
+#define BTF_KIND_ARRAY         3       /* Array        */
+#define BTF_KIND_STRUCT                4       /* Struct       */
+#define BTF_KIND_UNION         5       /* Union        */
+#define BTF_KIND_ENUM          6       /* Enumeration  */
+#define BTF_KIND_FWD           7       /* Forward      */
+#define BTF_KIND_TYPEDEF       8       /* Typedef      */
+#define BTF_KIND_VOLATILE      9       /* Volatile     */
+#define BTF_KIND_CONST         10      /* Const        */
+#define BTF_KIND_RESTRICT      11      /* Restrict     */
+#define BTF_KIND_MAX           11
+#define NR_BTF_KINDS           12
+
+/* For some specific BTF_KIND, "struct btf_type" is immediately
+ * followed by extra data.
+ */
+
+/* BTF_KIND_INT is followed by a u32 and the following
+ * is the 32 bits arrangement:
+ */
+#define BTF_INT_ENCODING(VAL)  (((VAL) & 0xff000000) >> 24)
+#define BTF_INT_OFFSET(VAL)    (((VAL  & 0x00ff0000)) >> 16)
+#define BTF_INT_BITS(VAL)      ((VAL)  & 0x0000ffff)
+
+/* Attributes stored in the BTF_INT_ENCODING */
+#define BTF_INT_SIGNED 0x1
+#define BTF_INT_CHAR   0x2
+#define BTF_INT_BOOL   0x4
+#define BTF_INT_VARARGS        0x8
+
+/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
+ * The exact number of btf_enum is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_enum {
+       __u32   name;
+       __s32   val;
+};
+
+/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
+struct btf_array {
+       __u32   type;
+       __u32   index_type;
+       __u32   nelems;
+};
+
+/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
+ * by multiple "struct btf_member".  The exact number
+ * of btf_member is stored in the vlen (of the info in
+ * "struct btf_type").
+ */
+struct btf_member {
+       __u32   name;
+       __u32   type;
+       __u32   offset; /* offset in bits */
+};
+
+#endif /* _UAPI__LINUX_BTF_H__ */
index a713fd23ec88214850af6311162ef6512aefd16f..35c485fa9ea36ba91d1fd5e7d979204ffc0751e8 100644 (file)
@@ -4,6 +4,7 @@ obj-y := core.o
 obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
 obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
 obj-$(CONFIG_BPF_SYSCALL) += disasm.o
+obj-$(CONFIG_BPF_SYSCALL) += btf.o
 ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_BPF_SYSCALL) += devmap.o
 obj-$(CONFIG_BPF_SYSCALL) += cpumap.o
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
new file mode 100644 (file)
index 0000000..26e9ed7
--- /dev/null
@@ -0,0 +1,915 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Facebook */
+
+#include <uapi/linux/btf.h>
+#include <uapi/linux/types.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/bpf_verifier.h>
+#include <linux/btf.h>
+
+/* BTF (BPF Type Format) is the meta data format which describes
+ * the data types of BPF program/map.  Hence, it basically focus
+ * on the C programming language which the modern BPF is primary
+ * using.
+ *
+ * ELF Section:
+ * ~~~~~~~~~~~
+ * The BTF data is stored under the ".BTF" ELF section
+ *
+ * struct btf_type:
+ * ~~~~~~~~~~~~~~~
+ * Each 'struct btf_type' object describes a C data type.
+ * Depending on the type it is describing, a 'struct btf_type'
+ * object may be followed by more data.  F.e.
+ * To describe an array, 'struct btf_type' is followed by
+ * 'struct btf_array'.
+ *
+ * 'struct btf_type' and any extra data following it are
+ * 4 bytes aligned.
+ *
+ * Type section:
+ * ~~~~~~~~~~~~~
+ * The BTF type section contains a list of 'struct btf_type' objects.
+ * Each one describes a C type.  Recall from the above section
+ * that a 'struct btf_type' object could be immediately followed by extra
+ * data in order to desribe some particular C types.
+ *
+ * type_id:
+ * ~~~~~~~
+ * Each btf_type object is identified by a type_id.  The type_id
+ * is implicitly implied by the location of the btf_type object in
+ * the BTF type section.  The first one has type_id 1.  The second
+ * one has type_id 2...etc.  Hence, an earlier btf_type has
+ * a smaller type_id.
+ *
+ * A btf_type object may refer to another btf_type object by using
+ * type_id (i.e. the "type" in the "struct btf_type").
+ *
+ * NOTE that we cannot assume any reference-order.
+ * A btf_type object can refer to an earlier btf_type object
+ * but it can also refer to a later btf_type object.
+ *
+ * For example, to describe "const void *".  A btf_type
+ * object describing "const" may refer to another btf_type
+ * object describing "void *".  This type-reference is done
+ * by specifying type_id:
+ *
+ * [1] CONST (anon) type_id=2
+ * [2] PTR (anon) type_id=0
+ *
+ * The above is the btf_verifier debug log:
+ *   - Each line started with "[?]" is a btf_type object
+ *   - [?] is the type_id of the btf_type object.
+ *   - CONST/PTR is the BTF_KIND_XXX
+ *   - "(anon)" is the name of the type.  It just
+ *     happens that CONST and PTR has no name.
+ *   - type_id=XXX is the 'u32 type' in btf_type
+ *
+ * NOTE: "void" has type_id 0
+ *
+ * String section:
+ * ~~~~~~~~~~~~~~
+ * The BTF string section contains the names used by the type section.
+ * Each string is referred by an "offset" from the beginning of the
+ * string section.
+ *
+ * Each string is '\0' terminated.
+ *
+ * The first character in the string section must be '\0'
+ * which is used to mean 'anonymous'. Some btf_type may not
+ * have a name.
+ */
+
+/* BTF verification:
+ *
+ * To verify BTF data, two passes are needed.
+ *
+ * Pass #1
+ * ~~~~~~~
+ * The first pass is to collect all btf_type objects to
+ * an array: "btf->types".
+ *
+ * Depending on the C type that a btf_type is describing,
+ * a btf_type may be followed by extra data.  We don't know
+ * how many btf_type is there, and more importantly we don't
+ * know where each btf_type is located in the type section.
+ *
+ * Without knowing the location of each type_id, most verifications
+ * cannot be done.  e.g. an earlier btf_type may refer to a later
+ * btf_type (recall the "const void *" above), so we cannot
+ * check this type-reference in the first pass.
+ *
+ * In the first pass, it still does some verifications (e.g.
+ * checking the name is a valid offset to the string section).
+ */
+
+#define BITS_PER_U64 (sizeof(u64) * BITS_PER_BYTE)
+#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
+#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
+#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
+#define BITS_ROUNDUP_BYTES(bits) \
+       (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
+
+/* 16MB for 64k structs and each has 16 members and
+ * a few MB spaces for the string section.
+ * The hard limit is S32_MAX.
+ */
+#define BTF_MAX_SIZE (16 * 1024 * 1024)
+/* 64k. We can raise it later. The hard limit is S32_MAX. */
+#define BTF_MAX_NR_TYPES 65535
+
+#define for_each_member(i, struct_type, member)                        \
+       for (i = 0, member = btf_type_member(struct_type);      \
+            i < btf_type_vlen(struct_type);                    \
+            i++, member++)
+
+struct btf {
+       union {
+               struct btf_header *hdr;
+               void *data;
+       };
+       struct btf_type **types;
+       const char *strings;
+       void *nohdr_data;
+       u32 nr_types;
+       u32 types_size;
+       u32 data_size;
+};
+
+struct btf_verifier_env {
+       struct btf *btf;
+       struct bpf_verifier_log log;
+       u32 log_type_id;
+};
+
+static const char * const btf_kind_str[NR_BTF_KINDS] = {
+       [BTF_KIND_UNKN]         = "UNKNOWN",
+       [BTF_KIND_INT]          = "INT",
+       [BTF_KIND_PTR]          = "PTR",
+       [BTF_KIND_ARRAY]        = "ARRAY",
+       [BTF_KIND_STRUCT]       = "STRUCT",
+       [BTF_KIND_UNION]        = "UNION",
+       [BTF_KIND_ENUM]         = "ENUM",
+       [BTF_KIND_FWD]          = "FWD",
+       [BTF_KIND_TYPEDEF]      = "TYPEDEF",
+       [BTF_KIND_VOLATILE]     = "VOLATILE",
+       [BTF_KIND_CONST]        = "CONST",
+       [BTF_KIND_RESTRICT]     = "RESTRICT",
+};
+
+struct btf_kind_operations {
+       s32 (*check_meta)(struct btf_verifier_env *env,
+                         const struct btf_type *t,
+                         u32 meta_left);
+       void (*log_details)(struct btf_verifier_env *env,
+                           const struct btf_type *t);
+};
+
+static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS];
+static struct btf_type btf_void;
+
+static const char *btf_int_encoding_str(u8 encoding)
+{
+       if (encoding == 0)
+               return "(none)";
+       else if (encoding == BTF_INT_SIGNED)
+               return "SIGNED";
+       else if (encoding == BTF_INT_CHAR)
+               return "CHAR";
+       else if (encoding == BTF_INT_BOOL)
+               return "BOOL";
+       else if (encoding == BTF_INT_VARARGS)
+               return "VARARGS";
+       else
+               return "UNKN";
+}
+
+static u16 btf_type_vlen(const struct btf_type *t)
+{
+       return BTF_INFO_VLEN(t->info);
+}
+
+static u32 btf_type_int(const struct btf_type *t)
+{
+       return *(u32 *)(t + 1);
+}
+
+static const struct btf_array *btf_type_array(const struct btf_type *t)
+{
+       return (const struct btf_array *)(t + 1);
+}
+
+static const struct btf_member *btf_type_member(const struct btf_type *t)
+{
+       return (const struct btf_member *)(t + 1);
+}
+
+static const struct btf_enum *btf_type_enum(const struct btf_type *t)
+{
+       return (const struct btf_enum *)(t + 1);
+}
+
+static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
+{
+       return kind_ops[BTF_INFO_KIND(t->info)];
+}
+
+static bool btf_name_offset_valid(const struct btf *btf, u32 offset)
+{
+       return !BTF_STR_TBL_ELF_ID(offset) &&
+               BTF_STR_OFFSET(offset) < btf->hdr->str_len;
+}
+
+static const char *btf_name_by_offset(const struct btf *btf, u32 offset)
+{
+       if (!BTF_STR_OFFSET(offset))
+               return "(anon)";
+       else if (BTF_STR_OFFSET(offset) < btf->hdr->str_len)
+               return &btf->strings[BTF_STR_OFFSET(offset)];
+       else
+               return "(invalid-name-offset)";
+}
+
+__printf(2, 3) static void __btf_verifier_log(struct bpf_verifier_log *log,
+                                             const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       bpf_verifier_vlog(log, fmt, args);
+       va_end(args);
+}
+
+__printf(2, 3) static void btf_verifier_log(struct btf_verifier_env *env,
+                                           const char *fmt, ...)
+{
+       struct bpf_verifier_log *log = &env->log;
+       va_list args;
+
+       if (!bpf_verifier_log_needed(log))
+               return;
+
+       va_start(args, fmt);
+       bpf_verifier_vlog(log, fmt, args);
+       va_end(args);
+}
+
+__printf(4, 5) static void __btf_verifier_log_type(struct btf_verifier_env *env,
+                                                  const struct btf_type *t,
+                                                  bool log_details,
+                                                  const char *fmt, ...)
+{
+       struct bpf_verifier_log *log = &env->log;
+       u8 kind = BTF_INFO_KIND(t->info);
+       struct btf *btf = env->btf;
+       va_list args;
+
+       if (!bpf_verifier_log_needed(log))
+               return;
+
+       __btf_verifier_log(log, "[%u] %s %s%s",
+                          env->log_type_id,
+                          btf_kind_str[kind],
+                          btf_name_by_offset(btf, t->name),
+                          log_details ? " " : "");
+
+       if (log_details)
+               btf_type_ops(t)->log_details(env, t);
+
+       if (fmt && *fmt) {
+               __btf_verifier_log(log, " ");
+               va_start(args, fmt);
+               bpf_verifier_vlog(log, fmt, args);
+               va_end(args);
+       }
+
+       __btf_verifier_log(log, "\n");
+}
+
+#define btf_verifier_log_type(env, t, ...) \
+       __btf_verifier_log_type((env), (t), true, __VA_ARGS__)
+#define btf_verifier_log_basic(env, t, ...) \
+       __btf_verifier_log_type((env), (t), false, __VA_ARGS__)
+
+__printf(4, 5)
+static void btf_verifier_log_member(struct btf_verifier_env *env,
+                                   const struct btf_type *struct_type,
+                                   const struct btf_member *member,
+                                   const char *fmt, ...)
+{
+       struct bpf_verifier_log *log = &env->log;
+       struct btf *btf = env->btf;
+       va_list args;
+
+       if (!bpf_verifier_log_needed(log))
+               return;
+
+       __btf_verifier_log(log, "\t%s type_id=%u bits_offset=%u",
+                          btf_name_by_offset(btf, member->name),
+                          member->type, member->offset);
+
+       if (fmt && *fmt) {
+               __btf_verifier_log(log, " ");
+               va_start(args, fmt);
+               bpf_verifier_vlog(log, fmt, args);
+               va_end(args);
+       }
+
+       __btf_verifier_log(log, "\n");
+}
+
+static void btf_verifier_log_hdr(struct btf_verifier_env *env)
+{
+       struct bpf_verifier_log *log = &env->log;
+       const struct btf *btf = env->btf;
+       const struct btf_header *hdr;
+
+       if (!bpf_verifier_log_needed(log))
+               return;
+
+       hdr = btf->hdr;
+       __btf_verifier_log(log, "magic: 0x%x\n", hdr->magic);
+       __btf_verifier_log(log, "version: %u\n", hdr->version);
+       __btf_verifier_log(log, "flags: 0x%x\n", hdr->flags);
+       __btf_verifier_log(log, "parent_label: %u\n", hdr->parent_label);
+       __btf_verifier_log(log, "parent_name: %u\n", hdr->parent_name);
+       __btf_verifier_log(log, "label_off: %u\n", hdr->label_off);
+       __btf_verifier_log(log, "object_off: %u\n", hdr->object_off);
+       __btf_verifier_log(log, "func_off: %u\n", hdr->func_off);
+       __btf_verifier_log(log, "type_off: %u\n", hdr->type_off);
+       __btf_verifier_log(log, "str_off: %u\n", hdr->str_off);
+       __btf_verifier_log(log, "str_len: %u\n", hdr->str_len);
+       __btf_verifier_log(log, "btf_total_size: %u\n", btf->data_size);
+}
+
+static int btf_add_type(struct btf_verifier_env *env, struct btf_type *t)
+{
+       struct btf *btf = env->btf;
+
+       /* < 2 because +1 for btf_void which is always in btf->types[0].
+        * btf_void is not accounted in btf->nr_types because btf_void
+        * does not come from the BTF file.
+        */
+       if (btf->types_size - btf->nr_types < 2) {
+               /* Expand 'types' array */
+
+               struct btf_type **new_types;
+               u32 expand_by, new_size;
+
+               if (btf->types_size == BTF_MAX_NR_TYPES) {
+                       btf_verifier_log(env, "Exceeded max num of types");
+                       return -E2BIG;
+               }
+
+               expand_by = max_t(u32, btf->types_size >> 2, 16);
+               new_size = min_t(u32, BTF_MAX_NR_TYPES,
+                                btf->types_size + expand_by);
+
+               new_types = kvzalloc(new_size * sizeof(*new_types),
+                                    GFP_KERNEL | __GFP_NOWARN);
+               if (!new_types)
+                       return -ENOMEM;
+
+               if (btf->nr_types == 0)
+                       new_types[0] = &btf_void;
+               else
+                       memcpy(new_types, btf->types,
+                              sizeof(*btf->types) * (btf->nr_types + 1));
+
+               kvfree(btf->types);
+               btf->types = new_types;
+               btf->types_size = new_size;
+       }
+
+       btf->types[++(btf->nr_types)] = t;
+
+       return 0;
+}
+
+static void btf_free(struct btf *btf)
+{
+       kvfree(btf->types);
+       kvfree(btf->data);
+       kfree(btf);
+}
+
+static void btf_verifier_env_free(struct btf_verifier_env *env)
+{
+       kfree(env);
+}
+
+static s32 btf_int_check_meta(struct btf_verifier_env *env,
+                             const struct btf_type *t,
+                             u32 meta_left)
+{
+       u32 int_data, nr_bits, meta_needed = sizeof(int_data);
+       u16 encoding;
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       int_data = btf_type_int(t);
+       nr_bits = BTF_INT_BITS(int_data) + BTF_INT_OFFSET(int_data);
+
+       if (nr_bits > BITS_PER_U64) {
+               btf_verifier_log_type(env, t, "nr_bits exceeds %zu",
+                                     BITS_PER_U64);
+               return -EINVAL;
+       }
+
+       if (BITS_ROUNDUP_BYTES(nr_bits) > t->size) {
+               btf_verifier_log_type(env, t, "nr_bits exceeds type_size");
+               return -EINVAL;
+       }
+
+       encoding = BTF_INT_ENCODING(int_data);
+       if (encoding &&
+           encoding != BTF_INT_SIGNED &&
+           encoding != BTF_INT_CHAR &&
+           encoding != BTF_INT_BOOL &&
+           encoding != BTF_INT_VARARGS) {
+               btf_verifier_log_type(env, t, "Unsupported encoding");
+               return -ENOTSUPP;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return meta_needed;
+}
+
+static void btf_int_log(struct btf_verifier_env *env,
+                       const struct btf_type *t)
+{
+       int int_data = btf_type_int(t);
+
+       btf_verifier_log(env,
+                        "size=%u bits_offset=%u nr_bits=%u encoding=%s",
+                        t->size, BTF_INT_OFFSET(int_data),
+                        BTF_INT_BITS(int_data),
+                        btf_int_encoding_str(BTF_INT_ENCODING(int_data)));
+}
+
+static const struct btf_kind_operations int_ops = {
+       .check_meta = btf_int_check_meta,
+       .log_details = btf_int_log,
+};
+
+static int btf_ref_type_check_meta(struct btf_verifier_env *env,
+                                  const struct btf_type *t,
+                                  u32 meta_left)
+{
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       if (BTF_TYPE_PARENT(t->type)) {
+               btf_verifier_log_type(env, t, "Invalid type_id");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return 0;
+}
+
+static void btf_ref_type_log(struct btf_verifier_env *env,
+                            const struct btf_type *t)
+{
+       btf_verifier_log(env, "type_id=%u", t->type);
+}
+
+static struct btf_kind_operations modifier_ops = {
+       .check_meta = btf_ref_type_check_meta,
+       .log_details = btf_ref_type_log,
+};
+
+static struct btf_kind_operations ptr_ops = {
+       .check_meta = btf_ref_type_check_meta,
+       .log_details = btf_ref_type_log,
+};
+
+static struct btf_kind_operations fwd_ops = {
+       .check_meta = btf_ref_type_check_meta,
+       .log_details = btf_ref_type_log,
+};
+
+static s32 btf_array_check_meta(struct btf_verifier_env *env,
+                               const struct btf_type *t,
+                               u32 meta_left)
+{
+       const struct btf_array *array = btf_type_array(t);
+       u32 meta_needed = sizeof(*array);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (btf_type_vlen(t)) {
+               btf_verifier_log_type(env, t, "vlen != 0");
+               return -EINVAL;
+       }
+
+       /* We are a little forgiving on array->index_type since
+        * the kernel is not using it.
+        */
+       /* Array elem cannot be in type void,
+        * so !array->type is not allowed.
+        */
+       if (!array->type || BTF_TYPE_PARENT(array->type)) {
+               btf_verifier_log_type(env, t, "Invalid type_id");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       return meta_needed;
+}
+
+static void btf_array_log(struct btf_verifier_env *env,
+                         const struct btf_type *t)
+{
+       const struct btf_array *array = btf_type_array(t);
+
+       btf_verifier_log(env, "type_id=%u index_type_id=%u nr_elems=%u",
+                        array->type, array->index_type, array->nelems);
+}
+
+static struct btf_kind_operations array_ops = {
+       .check_meta = btf_array_check_meta,
+       .log_details = btf_array_log,
+};
+
+static s32 btf_struct_check_meta(struct btf_verifier_env *env,
+                                const struct btf_type *t,
+                                u32 meta_left)
+{
+       bool is_union = BTF_INFO_KIND(t->info) == BTF_KIND_UNION;
+       const struct btf_member *member;
+       struct btf *btf = env->btf;
+       u32 struct_size = t->size;
+       u32 meta_needed;
+       u16 i;
+
+       meta_needed = btf_type_vlen(t) * sizeof(*member);
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       for_each_member(i, t, member) {
+               if (!btf_name_offset_valid(btf, member->name)) {
+                       btf_verifier_log_member(env, t, member,
+                                               "Invalid member name_offset:%u",
+                                               member->name);
+                       return -EINVAL;
+               }
+
+               /* A member cannot be in type void */
+               if (!member->type || BTF_TYPE_PARENT(member->type)) {
+                       btf_verifier_log_member(env, t, member,
+                                               "Invalid type_id");
+                       return -EINVAL;
+               }
+
+               if (is_union && member->offset) {
+                       btf_verifier_log_member(env, t, member,
+                                               "Invalid member bits_offset");
+                       return -EINVAL;
+               }
+
+               if (BITS_ROUNDUP_BYTES(member->offset) > struct_size) {
+                       btf_verifier_log_member(env, t, member,
+                                               "Memmber bits_offset exceeds its struct size");
+                       return -EINVAL;
+               }
+
+               btf_verifier_log_member(env, t, member, NULL);
+       }
+
+       return meta_needed;
+}
+
+static void btf_struct_log(struct btf_verifier_env *env,
+                          const struct btf_type *t)
+{
+       btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
+}
+
+static struct btf_kind_operations struct_ops = {
+       .check_meta = btf_struct_check_meta,
+       .log_details = btf_struct_log,
+};
+
+static s32 btf_enum_check_meta(struct btf_verifier_env *env,
+                              const struct btf_type *t,
+                              u32 meta_left)
+{
+       const struct btf_enum *enums = btf_type_enum(t);
+       struct btf *btf = env->btf;
+       u16 i, nr_enums;
+       u32 meta_needed;
+
+       nr_enums = btf_type_vlen(t);
+       meta_needed = nr_enums * sizeof(*enums);
+
+       if (meta_left < meta_needed) {
+               btf_verifier_log_basic(env, t,
+                                      "meta_left:%u meta_needed:%u",
+                                      meta_left, meta_needed);
+               return -EINVAL;
+       }
+
+       if (t->size != sizeof(int)) {
+               btf_verifier_log_type(env, t, "Expected size:%zu",
+                                     sizeof(int));
+               return -EINVAL;
+       }
+
+       btf_verifier_log_type(env, t, NULL);
+
+       for (i = 0; i < nr_enums; i++) {
+               if (!btf_name_offset_valid(btf, enums[i].name)) {
+                       btf_verifier_log(env, "\tInvalid name_offset:%u",
+                                        enums[i].name);
+                       return -EINVAL;
+               }
+
+               btf_verifier_log(env, "\t%s val=%d\n",
+                                btf_name_by_offset(btf, enums[i].name),
+                                enums[i].val);
+       }
+
+       return meta_needed;
+}
+
+static void btf_enum_log(struct btf_verifier_env *env,
+                        const struct btf_type *t)
+{
+       btf_verifier_log(env, "size=%u vlen=%u", t->size, btf_type_vlen(t));
+}
+
+static struct btf_kind_operations enum_ops = {
+       .check_meta = btf_enum_check_meta,
+       .log_details = btf_enum_log,
+};
+
+static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
+       [BTF_KIND_INT] = &int_ops,
+       [BTF_KIND_PTR] = &ptr_ops,
+       [BTF_KIND_ARRAY] = &array_ops,
+       [BTF_KIND_STRUCT] = &struct_ops,
+       [BTF_KIND_UNION] = &struct_ops,
+       [BTF_KIND_ENUM] = &enum_ops,
+       [BTF_KIND_FWD] = &fwd_ops,
+       [BTF_KIND_TYPEDEF] = &modifier_ops,
+       [BTF_KIND_VOLATILE] = &modifier_ops,
+       [BTF_KIND_CONST] = &modifier_ops,
+       [BTF_KIND_RESTRICT] = &modifier_ops,
+};
+
+static s32 btf_check_meta(struct btf_verifier_env *env,
+                         const struct btf_type *t,
+                         u32 meta_left)
+{
+       u32 saved_meta_left = meta_left;
+       s32 var_meta_size;
+
+       if (meta_left < sizeof(*t)) {
+               btf_verifier_log(env, "[%u] meta_left:%u meta_needed:%zu",
+                                env->log_type_id, meta_left, sizeof(*t));
+               return -EINVAL;
+       }
+       meta_left -= sizeof(*t);
+
+       if (BTF_INFO_KIND(t->info) > BTF_KIND_MAX ||
+           BTF_INFO_KIND(t->info) == BTF_KIND_UNKN) {
+               btf_verifier_log(env, "[%u] Invalid kind:%u",
+                                env->log_type_id, BTF_INFO_KIND(t->info));
+               return -EINVAL;
+       }
+
+       if (!btf_name_offset_valid(env->btf, t->name)) {
+               btf_verifier_log(env, "[%u] Invalid name_offset:%u",
+                                env->log_type_id, t->name);
+               return -EINVAL;
+       }
+
+       var_meta_size = btf_type_ops(t)->check_meta(env, t, meta_left);
+       if (var_meta_size < 0)
+               return var_meta_size;
+
+       meta_left -= var_meta_size;
+
+       return saved_meta_left - meta_left;
+}
+
+static int btf_check_all_metas(struct btf_verifier_env *env)
+{
+       struct btf *btf = env->btf;
+       struct btf_header *hdr;
+       void *cur, *end;
+
+       hdr = btf->hdr;
+       cur = btf->nohdr_data + hdr->type_off;
+       end = btf->nohdr_data + hdr->str_off;
+
+       env->log_type_id = 1;
+       while (cur < end) {
+               struct btf_type *t = cur;
+               s32 meta_size;
+
+               meta_size = btf_check_meta(env, t, end - cur);
+               if (meta_size < 0)
+                       return meta_size;
+
+               btf_add_type(env, t);
+               cur += meta_size;
+               env->log_type_id++;
+       }
+
+       return 0;
+}
+
+static int btf_parse_type_sec(struct btf_verifier_env *env)
+{
+       return btf_check_all_metas(env);
+}
+
+static int btf_parse_str_sec(struct btf_verifier_env *env)
+{
+       const struct btf_header *hdr;
+       struct btf *btf = env->btf;
+       const char *start, *end;
+
+       hdr = btf->hdr;
+       start = btf->nohdr_data + hdr->str_off;
+       end = start + hdr->str_len;
+
+       if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
+           start[0] || end[-1]) {
+               btf_verifier_log(env, "Invalid string section");
+               return -EINVAL;
+       }
+
+       btf->strings = start;
+
+       return 0;
+}
+
+static int btf_parse_hdr(struct btf_verifier_env *env)
+{
+       const struct btf_header *hdr;
+       struct btf *btf = env->btf;
+       u32 meta_left;
+
+       if (btf->data_size < sizeof(*hdr)) {
+               btf_verifier_log(env, "btf_header not found");
+               return -EINVAL;
+       }
+
+       btf_verifier_log_hdr(env);
+
+       hdr = btf->hdr;
+       if (hdr->magic != BTF_MAGIC) {
+               btf_verifier_log(env, "Invalid magic");
+               return -EINVAL;
+       }
+
+       if (hdr->version != BTF_VERSION) {
+               btf_verifier_log(env, "Unsupported version");
+               return -ENOTSUPP;
+       }
+
+       if (hdr->flags) {
+               btf_verifier_log(env, "Unsupported flags");
+               return -ENOTSUPP;
+       }
+
+       meta_left = btf->data_size - sizeof(*hdr);
+       if (!meta_left) {
+               btf_verifier_log(env, "No data");
+               return -EINVAL;
+       }
+
+       if (meta_left < hdr->type_off || hdr->str_off <= hdr->type_off ||
+           /* Type section must align to 4 bytes */
+           hdr->type_off & (sizeof(u32) - 1)) {
+               btf_verifier_log(env, "Invalid type_off");
+               return -EINVAL;
+       }
+
+       if (meta_left < hdr->str_off ||
+           meta_left - hdr->str_off < hdr->str_len) {
+               btf_verifier_log(env, "Invalid str_off or str_len");
+               return -EINVAL;
+       }
+
+       btf->nohdr_data = btf->hdr + 1;
+
+       return 0;
+}
+
+static struct btf *btf_parse(void __user *btf_data, u32 btf_data_size,
+                            u32 log_level, char __user *log_ubuf, u32 log_size)
+{
+       struct btf_verifier_env *env = NULL;
+       struct bpf_verifier_log *log;
+       struct btf *btf = NULL;
+       u8 *data;
+       int err;
+
+       if (btf_data_size > BTF_MAX_SIZE)
+               return ERR_PTR(-E2BIG);
+
+       env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN);
+       if (!env)
+               return ERR_PTR(-ENOMEM);
+
+       log = &env->log;
+       if (log_level || log_ubuf || log_size) {
+               /* user requested verbose verifier output
+                * and supplied buffer to store the verification trace
+                */
+               log->level = log_level;
+               log->ubuf = log_ubuf;
+               log->len_total = log_size;
+
+               /* log attributes have to be sane */
+               if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 ||
+                   !log->level || !log->ubuf) {
+                       err = -EINVAL;
+                       goto errout;
+               }
+       }
+
+       btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN);
+       if (!btf) {
+               err = -ENOMEM;
+               goto errout;
+       }
+
+       data = kvmalloc(btf_data_size, GFP_KERNEL | __GFP_NOWARN);
+       if (!data) {
+               err = -ENOMEM;
+               goto errout;
+       }
+
+       btf->data = data;
+       btf->data_size = btf_data_size;
+
+       if (copy_from_user(data, btf_data, btf_data_size)) {
+               err = -EFAULT;
+               goto errout;
+       }
+
+       env->btf = btf;
+
+       err = btf_parse_hdr(env);
+       if (err)
+               goto errout;
+
+       err = btf_parse_str_sec(env);
+       if (err)
+               goto errout;
+
+       err = btf_parse_type_sec(env);
+       if (err)
+               goto errout;
+
+       if (!err && log->level && bpf_verifier_log_full(log)) {
+               err = -ENOSPC;
+               goto errout;
+       }
+
+       if (!err) {
+               btf_verifier_env_free(env);
+               return btf;
+       }
+
+errout:
+       btf_verifier_env_free(env);
+       if (btf)
+               btf_free(btf);
+       return ERR_PTR(err);
+}