mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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
					
				
					 3 changed files with 1046 additions and 0 deletions
				
			
		
							
								
								
									
										130
									
								
								include/uapi/linux/btf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								include/uapi/linux/btf.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
|  |  | |||
							
								
								
									
										915
									
								
								kernel/bpf/btf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										915
									
								
								kernel/bpf/btf.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -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 a new issue
	
	 Martin KaFai Lau
						Martin KaFai Lau