bpf: btf: Introduce BPF Type Format (BTF)
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>
This commit is contained in:
parent
97e19cce05
commit
69b693f0ae
|
@ -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__ */
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue