mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	bpf: Introduce BPF_PROG_TYPE_STRUCT_OPS
This patch allows the kernel's struct ops (i.e. func ptr) to be implemented in BPF. The first use case in this series is the "struct tcp_congestion_ops" which will be introduced in a latter patch. This patch introduces a new prog type BPF_PROG_TYPE_STRUCT_OPS. The BPF_PROG_TYPE_STRUCT_OPS prog is verified against a particular func ptr of a kernel struct. The attr->attach_btf_id is the btf id of a kernel struct. The attr->expected_attach_type is the member "index" of that kernel struct. The first member of a struct starts with member index 0. That will avoid ambiguity when a kernel struct has multiple func ptrs with the same func signature. For example, a BPF_PROG_TYPE_STRUCT_OPS prog is written to implement the "init" func ptr of the "struct tcp_congestion_ops". The attr->attach_btf_id is the btf id of the "struct tcp_congestion_ops" of the _running_ kernel. The attr->expected_attach_type is 3. The ctx of BPF_PROG_TYPE_STRUCT_OPS is an array of u64 args saved by arch_prepare_bpf_trampoline that will be done in the next patch when introducing BPF_MAP_TYPE_STRUCT_OPS. "struct bpf_struct_ops" is introduced as a common interface for the kernel struct that supports BPF_PROG_TYPE_STRUCT_OPS prog. The supporting kernel struct will need to implement an instance of the "struct bpf_struct_ops". The supporting kernel struct also needs to implement a bpf_verifier_ops. During BPF_PROG_LOAD, bpf_struct_ops_find() will find the right bpf_verifier_ops by searching the attr->attach_btf_id. A new "btf_struct_access" is also added to the bpf_verifier_ops such that the supporting kernel struct can optionally provide its own specific check on accessing the func arg (e.g. provide limited write access). After btf_vmlinux is parsed, the new bpf_struct_ops_init() is called to initialize some values (e.g. the btf id of the supporting kernel struct) and it can only be done once the btf_vmlinux is available. The R0 checks at BPF_EXIT is excluded for the BPF_PROG_TYPE_STRUCT_OPS prog if the return type of the prog->aux->attach_func_proto is "void". Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Acked-by: Yonghong Song <yhs@fb.com> Link: https://lore.kernel.org/bpf/20200109003503.3855825-1-kafai@fb.com
This commit is contained in:
		
							parent
							
								
									976aba002f
								
							
						
					
					
						commit
						27ae7997a6
					
				
					 10 changed files with 373 additions and 63 deletions
				
			
		|  | @ -349,6 +349,10 @@ struct bpf_verifier_ops { | ||||||
| 				  const struct bpf_insn *src, | 				  const struct bpf_insn *src, | ||||||
| 				  struct bpf_insn *dst, | 				  struct bpf_insn *dst, | ||||||
| 				  struct bpf_prog *prog, u32 *target_size); | 				  struct bpf_prog *prog, u32 *target_size); | ||||||
|  | 	int (*btf_struct_access)(struct bpf_verifier_log *log, | ||||||
|  | 				 const struct btf_type *t, int off, int size, | ||||||
|  | 				 enum bpf_access_type atype, | ||||||
|  | 				 u32 *next_btf_id); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct bpf_prog_offload_ops { | struct bpf_prog_offload_ops { | ||||||
|  | @ -668,6 +672,32 @@ struct bpf_array_aux { | ||||||
| 	struct work_struct work; | 	struct work_struct work; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct btf_type; | ||||||
|  | struct btf_member; | ||||||
|  | 
 | ||||||
|  | #define BPF_STRUCT_OPS_MAX_NR_MEMBERS 64 | ||||||
|  | struct bpf_struct_ops { | ||||||
|  | 	const struct bpf_verifier_ops *verifier_ops; | ||||||
|  | 	int (*init)(struct btf *btf); | ||||||
|  | 	int (*check_member)(const struct btf_type *t, | ||||||
|  | 			    const struct btf_member *member); | ||||||
|  | 	const struct btf_type *type; | ||||||
|  | 	const char *name; | ||||||
|  | 	struct btf_func_model func_models[BPF_STRUCT_OPS_MAX_NR_MEMBERS]; | ||||||
|  | 	u32 type_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #if defined(CONFIG_BPF_JIT) && defined(CONFIG_BPF_SYSCALL) | ||||||
|  | const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id); | ||||||
|  | void bpf_struct_ops_init(struct btf *btf); | ||||||
|  | #else | ||||||
|  | static inline const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id) | ||||||
|  | { | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | static inline void bpf_struct_ops_init(struct btf *btf) { } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| struct bpf_array { | struct bpf_array { | ||||||
| 	struct bpf_map map; | 	struct bpf_map map; | ||||||
| 	u32 elem_size; | 	u32 elem_size; | ||||||
|  |  | ||||||
|  | @ -65,6 +65,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LIRC_MODE2, lirc_mode2, | ||||||
| BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport, | BPF_PROG_TYPE(BPF_PROG_TYPE_SK_REUSEPORT, sk_reuseport, | ||||||
| 	      struct sk_reuseport_md, struct sk_reuseport_kern) | 	      struct sk_reuseport_md, struct sk_reuseport_kern) | ||||||
| #endif | #endif | ||||||
|  | #if defined(CONFIG_BPF_JIT) | ||||||
|  | BPF_PROG_TYPE(BPF_PROG_TYPE_STRUCT_OPS, bpf_struct_ops, | ||||||
|  | 	      void *, void *) | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) | BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) | ||||||
| BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) | BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) | ||||||
|  |  | ||||||
|  | @ -53,6 +53,18 @@ bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s, | ||||||
| 			   u32 expected_offset, u32 expected_size); | 			   u32 expected_offset, u32 expected_size); | ||||||
| int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); | int btf_find_spin_lock(const struct btf *btf, const struct btf_type *t); | ||||||
| bool btf_type_is_void(const struct btf_type *t); | bool btf_type_is_void(const struct btf_type *t); | ||||||
|  | s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); | ||||||
|  | const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, | ||||||
|  | 					       u32 id, u32 *res_id); | ||||||
|  | const struct btf_type *btf_type_resolve_ptr(const struct btf *btf, | ||||||
|  | 					    u32 id, u32 *res_id); | ||||||
|  | const struct btf_type *btf_type_resolve_func_ptr(const struct btf *btf, | ||||||
|  | 						 u32 id, u32 *res_id); | ||||||
|  | 
 | ||||||
|  | #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++) | ||||||
| 
 | 
 | ||||||
| static inline bool btf_type_is_ptr(const struct btf_type *t) | static inline bool btf_type_is_ptr(const struct btf_type *t) | ||||||
| { | { | ||||||
|  | @ -84,6 +96,28 @@ static inline bool btf_type_is_func_proto(const struct btf_type *t) | ||||||
| 	return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO; | 	return BTF_INFO_KIND(t->info) == BTF_KIND_FUNC_PROTO; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline u16 btf_type_vlen(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return BTF_INFO_VLEN(t->info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool btf_type_kflag(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return BTF_INFO_KFLAG(t->info); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline u32 btf_member_bitfield_size(const struct btf_type *struct_type, | ||||||
|  | 					   const struct btf_member *member) | ||||||
|  | { | ||||||
|  | 	return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset) | ||||||
|  | 					   : 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline const struct btf_member *btf_type_member(const struct btf_type *t) | ||||||
|  | { | ||||||
|  | 	return (const struct btf_member *)(t + 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #ifdef CONFIG_BPF_SYSCALL | #ifdef CONFIG_BPF_SYSCALL | ||||||
| const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); | const struct btf_type *btf_type_by_id(const struct btf *btf, u32 type_id); | ||||||
| const char *btf_name_by_offset(const struct btf *btf, u32 offset); | const char *btf_name_by_offset(const struct btf *btf, u32 offset); | ||||||
|  |  | ||||||
|  | @ -174,6 +174,7 @@ enum bpf_prog_type { | ||||||
| 	BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, | 	BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE, | ||||||
| 	BPF_PROG_TYPE_CGROUP_SOCKOPT, | 	BPF_PROG_TYPE_CGROUP_SOCKOPT, | ||||||
| 	BPF_PROG_TYPE_TRACING, | 	BPF_PROG_TYPE_TRACING, | ||||||
|  | 	BPF_PROG_TYPE_STRUCT_OPS, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum bpf_attach_type { | enum bpf_attach_type { | ||||||
|  |  | ||||||
|  | @ -27,3 +27,6 @@ endif | ||||||
| ifeq ($(CONFIG_SYSFS),y) | ifeq ($(CONFIG_SYSFS),y) | ||||||
| obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o | obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o | ||||||
| endif | endif | ||||||
|  | ifeq ($(CONFIG_BPF_JIT),y) | ||||||
|  | obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o | ||||||
|  | endif | ||||||
|  |  | ||||||
							
								
								
									
										121
									
								
								kernel/bpf/bpf_struct_ops.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								kernel/bpf/bpf_struct_ops.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,121 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-only
 | ||||||
|  | /* Copyright (c) 2019 Facebook */ | ||||||
|  | 
 | ||||||
|  | #include <linux/bpf.h> | ||||||
|  | #include <linux/bpf_verifier.h> | ||||||
|  | #include <linux/btf.h> | ||||||
|  | #include <linux/filter.h> | ||||||
|  | #include <linux/slab.h> | ||||||
|  | #include <linux/numa.h> | ||||||
|  | #include <linux/seq_file.h> | ||||||
|  | #include <linux/refcount.h> | ||||||
|  | 
 | ||||||
|  | #define BPF_STRUCT_OPS_TYPE(_name)				\ | ||||||
|  | extern struct bpf_struct_ops bpf_##_name; | ||||||
|  | #include "bpf_struct_ops_types.h" | ||||||
|  | #undef BPF_STRUCT_OPS_TYPE | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  | #define BPF_STRUCT_OPS_TYPE(_name) BPF_STRUCT_OPS_TYPE_##_name, | ||||||
|  | #include "bpf_struct_ops_types.h" | ||||||
|  | #undef BPF_STRUCT_OPS_TYPE | ||||||
|  | 	__NR_BPF_STRUCT_OPS_TYPE, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct bpf_struct_ops * const bpf_struct_ops[] = { | ||||||
|  | #define BPF_STRUCT_OPS_TYPE(_name)				\ | ||||||
|  | 	[BPF_STRUCT_OPS_TYPE_##_name] = &bpf_##_name, | ||||||
|  | #include "bpf_struct_ops_types.h" | ||||||
|  | #undef BPF_STRUCT_OPS_TYPE | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = { | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const struct bpf_prog_ops bpf_struct_ops_prog_ops = { | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void bpf_struct_ops_init(struct btf *btf) | ||||||
|  | { | ||||||
|  | 	const struct btf_member *member; | ||||||
|  | 	struct bpf_struct_ops *st_ops; | ||||||
|  | 	struct bpf_verifier_log log = {}; | ||||||
|  | 	const struct btf_type *t; | ||||||
|  | 	const char *mname; | ||||||
|  | 	s32 type_id; | ||||||
|  | 	u32 i, j; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) { | ||||||
|  | 		st_ops = bpf_struct_ops[i]; | ||||||
|  | 
 | ||||||
|  | 		type_id = btf_find_by_name_kind(btf, st_ops->name, | ||||||
|  | 						BTF_KIND_STRUCT); | ||||||
|  | 		if (type_id < 0) { | ||||||
|  | 			pr_warn("Cannot find struct %s in btf_vmlinux\n", | ||||||
|  | 				st_ops->name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		t = btf_type_by_id(btf, type_id); | ||||||
|  | 		if (btf_type_vlen(t) > BPF_STRUCT_OPS_MAX_NR_MEMBERS) { | ||||||
|  | 			pr_warn("Cannot support #%u members in struct %s\n", | ||||||
|  | 				btf_type_vlen(t), st_ops->name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for_each_member(j, t, member) { | ||||||
|  | 			const struct btf_type *func_proto; | ||||||
|  | 
 | ||||||
|  | 			mname = btf_name_by_offset(btf, member->name_off); | ||||||
|  | 			if (!*mname) { | ||||||
|  | 				pr_warn("anon member in struct %s is not supported\n", | ||||||
|  | 					st_ops->name); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (btf_member_bitfield_size(t, member)) { | ||||||
|  | 				pr_warn("bit field member %s in struct %s is not supported\n", | ||||||
|  | 					mname, st_ops->name); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			func_proto = btf_type_resolve_func_ptr(btf, | ||||||
|  | 							       member->type, | ||||||
|  | 							       NULL); | ||||||
|  | 			if (func_proto && | ||||||
|  | 			    btf_distill_func_proto(&log, btf, | ||||||
|  | 						   func_proto, mname, | ||||||
|  | 						   &st_ops->func_models[j])) { | ||||||
|  | 				pr_warn("Error in parsing func ptr %s in struct %s\n", | ||||||
|  | 					mname, st_ops->name); | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (j == btf_type_vlen(t)) { | ||||||
|  | 			if (st_ops->init(btf)) { | ||||||
|  | 				pr_warn("Error in init bpf_struct_ops %s\n", | ||||||
|  | 					st_ops->name); | ||||||
|  | 			} else { | ||||||
|  | 				st_ops->type_id = type_id; | ||||||
|  | 				st_ops->type = t; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern struct btf *btf_vmlinux; | ||||||
|  | 
 | ||||||
|  | const struct bpf_struct_ops *bpf_struct_ops_find(u32 type_id) | ||||||
|  | { | ||||||
|  | 	unsigned int i; | ||||||
|  | 
 | ||||||
|  | 	if (!type_id || !btf_vmlinux) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(bpf_struct_ops); i++) { | ||||||
|  | 		if (bpf_struct_ops[i]->type_id == type_id) | ||||||
|  | 			return bpf_struct_ops[i]; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								kernel/bpf/bpf_struct_ops_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								kernel/bpf/bpf_struct_ops_types.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /* internal file - do not include directly */ | ||||||
|  | 
 | ||||||
|  | /* To be filled in a later patch */ | ||||||
|  | @ -180,11 +180,6 @@ | ||||||
|  */ |  */ | ||||||
| #define BTF_MAX_SIZE (16 * 1024 * 1024) | #define BTF_MAX_SIZE (16 * 1024 * 1024) | ||||||
| 
 | 
 | ||||||
| #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++) |  | ||||||
| 
 |  | ||||||
| #define for_each_member_from(i, from, struct_type, member)		\ | #define for_each_member_from(i, from, struct_type, member)		\ | ||||||
| 	for (i = from, member = btf_type_member(struct_type) + from;	\ | 	for (i = from, member = btf_type_member(struct_type) + from;	\ | ||||||
| 	     i < btf_type_vlen(struct_type);				\ | 	     i < btf_type_vlen(struct_type);				\ | ||||||
|  | @ -382,6 +377,65 @@ static bool btf_type_is_datasec(const struct btf_type *t) | ||||||
| 	return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC; | 	return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *t; | ||||||
|  | 	const char *tname; | ||||||
|  | 	u32 i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 1; i <= btf->nr_types; i++) { | ||||||
|  | 		t = btf->types[i]; | ||||||
|  | 		if (BTF_INFO_KIND(t->info) != kind) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		tname = btf_name_by_offset(btf, t->name_off); | ||||||
|  | 		if (!strcmp(tname, name)) | ||||||
|  | 			return i; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -ENOENT; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, | ||||||
|  | 					       u32 id, u32 *res_id) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *t = btf_type_by_id(btf, id); | ||||||
|  | 
 | ||||||
|  | 	while (btf_type_is_modifier(t)) { | ||||||
|  | 		id = t->type; | ||||||
|  | 		t = btf_type_by_id(btf, t->type); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (res_id) | ||||||
|  | 		*res_id = id; | ||||||
|  | 
 | ||||||
|  | 	return t; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct btf_type *btf_type_resolve_ptr(const struct btf *btf, | ||||||
|  | 					    u32 id, u32 *res_id) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *t; | ||||||
|  | 
 | ||||||
|  | 	t = btf_type_skip_modifiers(btf, id, NULL); | ||||||
|  | 	if (!btf_type_is_ptr(t)) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	return btf_type_skip_modifiers(btf, t->type, res_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct btf_type *btf_type_resolve_func_ptr(const struct btf *btf, | ||||||
|  | 						 u32 id, u32 *res_id) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *ptype; | ||||||
|  | 
 | ||||||
|  | 	ptype = btf_type_resolve_ptr(btf, id, res_id); | ||||||
|  | 	if (ptype && btf_type_is_func_proto(ptype)) | ||||||
|  | 		return ptype; | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Types that act only as a source, not sink or intermediate
 | /* Types that act only as a source, not sink or intermediate
 | ||||||
|  * type when resolving. |  * type when resolving. | ||||||
|  */ |  */ | ||||||
|  | @ -446,16 +500,6 @@ static const char *btf_int_encoding_str(u8 encoding) | ||||||
| 		return "UNKN"; | 		return "UNKN"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static u16 btf_type_vlen(const struct btf_type *t) |  | ||||||
| { |  | ||||||
| 	return BTF_INFO_VLEN(t->info); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static bool btf_type_kflag(const struct btf_type *t) |  | ||||||
| { |  | ||||||
| 	return BTF_INFO_KFLAG(t->info); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static u32 btf_member_bit_offset(const struct btf_type *struct_type, | static u32 btf_member_bit_offset(const struct btf_type *struct_type, | ||||||
| 			     const struct btf_member *member) | 			     const struct btf_member *member) | ||||||
| { | { | ||||||
|  | @ -463,13 +507,6 @@ static u32 btf_member_bit_offset(const struct btf_type *struct_type, | ||||||
| 					   : member->offset; | 					   : member->offset; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static u32 btf_member_bitfield_size(const struct btf_type *struct_type, |  | ||||||
| 				    const struct btf_member *member) |  | ||||||
| { |  | ||||||
| 	return btf_type_kflag(struct_type) ? BTF_MEMBER_BITFIELD_SIZE(member->offset) |  | ||||||
| 					   : 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static u32 btf_type_int(const struct btf_type *t) | static u32 btf_type_int(const struct btf_type *t) | ||||||
| { | { | ||||||
| 	return *(u32 *)(t + 1); | 	return *(u32 *)(t + 1); | ||||||
|  | @ -480,11 +517,6 @@ static const struct btf_array *btf_type_array(const struct btf_type *t) | ||||||
| 	return (const struct btf_array *)(t + 1); | 	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) | static const struct btf_enum *btf_type_enum(const struct btf_type *t) | ||||||
| { | { | ||||||
| 	return (const struct btf_enum *)(t + 1); | 	return (const struct btf_enum *)(t + 1); | ||||||
|  | @ -3605,6 +3637,8 @@ struct btf *btf_parse_vmlinux(void) | ||||||
| 		goto errout; | 		goto errout; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	bpf_struct_ops_init(btf); | ||||||
|  | 
 | ||||||
| 	btf_verifier_env_free(env); | 	btf_verifier_env_free(env); | ||||||
| 	refcount_set(&btf->refcnt, 1); | 	refcount_set(&btf->refcnt, 1); | ||||||
| 	return btf; | 	return btf; | ||||||
|  |  | ||||||
|  | @ -1672,17 +1672,22 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type, | ||||||
| 			   enum bpf_attach_type expected_attach_type, | 			   enum bpf_attach_type expected_attach_type, | ||||||
| 			   u32 btf_id, u32 prog_fd) | 			   u32 btf_id, u32 prog_fd) | ||||||
| { | { | ||||||
| 	switch (prog_type) { | 	if (btf_id) { | ||||||
| 	case BPF_PROG_TYPE_TRACING: |  | ||||||
| 		if (btf_id > BTF_MAX_TYPE) | 		if (btf_id > BTF_MAX_TYPE) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		break; | 
 | ||||||
| 	default: | 		switch (prog_type) { | ||||||
| 		if (btf_id || prog_fd) | 		case BPF_PROG_TYPE_TRACING: | ||||||
|  | 		case BPF_PROG_TYPE_STRUCT_OPS: | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 		break; | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
| 	switch (prog_type) { | 	switch (prog_type) { | ||||||
| 	case BPF_PROG_TYPE_CGROUP_SOCK: | 	case BPF_PROG_TYPE_CGROUP_SOCK: | ||||||
| 		switch (expected_attach_type) { | 		switch (expected_attach_type) { | ||||||
|  |  | ||||||
|  | @ -2859,11 +2859,6 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, | ||||||
| 	u32 btf_id; | 	u32 btf_id; | ||||||
| 	int ret; | 	int ret; | ||||||
| 
 | 
 | ||||||
| 	if (atype != BPF_READ) { |  | ||||||
| 		verbose(env, "only read is supported\n"); |  | ||||||
| 		return -EACCES; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (off < 0) { | 	if (off < 0) { | ||||||
| 		verbose(env, | 		verbose(env, | ||||||
| 			"R%d is ptr_%s invalid negative access: off=%d\n", | 			"R%d is ptr_%s invalid negative access: off=%d\n", | ||||||
|  | @ -2880,17 +2875,32 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, | ||||||
| 		return -EACCES; | 		return -EACCES; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ret = btf_struct_access(&env->log, t, off, size, atype, &btf_id); | 	if (env->ops->btf_struct_access) { | ||||||
|  | 		ret = env->ops->btf_struct_access(&env->log, t, off, size, | ||||||
|  | 						  atype, &btf_id); | ||||||
|  | 	} else { | ||||||
|  | 		if (atype != BPF_READ) { | ||||||
|  | 			verbose(env, "only read is supported\n"); | ||||||
|  | 			return -EACCES; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ret = btf_struct_access(&env->log, t, off, size, atype, | ||||||
|  | 					&btf_id); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (ret < 0) | 	if (ret < 0) | ||||||
| 		return ret; | 		return ret; | ||||||
| 
 | 
 | ||||||
| 	if (ret == SCALAR_VALUE) { | 	if (atype == BPF_READ) { | ||||||
| 		mark_reg_unknown(env, regs, value_regno); | 		if (ret == SCALAR_VALUE) { | ||||||
| 		return 0; | 			mark_reg_unknown(env, regs, value_regno); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 		mark_reg_known_zero(env, regs, value_regno); | ||||||
|  | 		regs[value_regno].type = PTR_TO_BTF_ID; | ||||||
|  | 		regs[value_regno].btf_id = btf_id; | ||||||
| 	} | 	} | ||||||
| 	mark_reg_known_zero(env, regs, value_regno); | 
 | ||||||
| 	regs[value_regno].type = PTR_TO_BTF_ID; |  | ||||||
| 	regs[value_regno].btf_id = btf_id; |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -6349,8 +6359,30 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) | ||||||
| static int check_return_code(struct bpf_verifier_env *env) | static int check_return_code(struct bpf_verifier_env *env) | ||||||
| { | { | ||||||
| 	struct tnum enforce_attach_type_range = tnum_unknown; | 	struct tnum enforce_attach_type_range = tnum_unknown; | ||||||
|  | 	const struct bpf_prog *prog = env->prog; | ||||||
| 	struct bpf_reg_state *reg; | 	struct bpf_reg_state *reg; | ||||||
| 	struct tnum range = tnum_range(0, 1); | 	struct tnum range = tnum_range(0, 1); | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	/* The struct_ops func-ptr's return type could be "void" */ | ||||||
|  | 	if (env->prog->type == BPF_PROG_TYPE_STRUCT_OPS && | ||||||
|  | 	    !prog->aux->attach_func_proto->type) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	/* eBPF calling convetion is such that R0 is used
 | ||||||
|  | 	 * to return the value from eBPF program. | ||||||
|  | 	 * Make sure that it's readable at this time | ||||||
|  | 	 * of bpf_exit, which means that program wrote | ||||||
|  | 	 * something into it earlier | ||||||
|  | 	 */ | ||||||
|  | 	err = check_reg_arg(env, BPF_REG_0, SRC_OP); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  | 
 | ||||||
|  | 	if (is_pointer_value(env, BPF_REG_0)) { | ||||||
|  | 		verbose(env, "R0 leaks addr as return value\n"); | ||||||
|  | 		return -EACCES; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	switch (env->prog->type) { | 	switch (env->prog->type) { | ||||||
| 	case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: | 	case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: | ||||||
|  | @ -8016,21 +8048,6 @@ static int do_check(struct bpf_verifier_env *env) | ||||||
| 				if (err) | 				if (err) | ||||||
| 					return err; | 					return err; | ||||||
| 
 | 
 | ||||||
| 				/* eBPF calling convetion is such that R0 is used
 |  | ||||||
| 				 * to return the value from eBPF program. |  | ||||||
| 				 * Make sure that it's readable at this time |  | ||||||
| 				 * of bpf_exit, which means that program wrote |  | ||||||
| 				 * something into it earlier |  | ||||||
| 				 */ |  | ||||||
| 				err = check_reg_arg(env, BPF_REG_0, SRC_OP); |  | ||||||
| 				if (err) |  | ||||||
| 					return err; |  | ||||||
| 
 |  | ||||||
| 				if (is_pointer_value(env, BPF_REG_0)) { |  | ||||||
| 					verbose(env, "R0 leaks addr as return value\n"); |  | ||||||
| 					return -EACCES; |  | ||||||
| 				} |  | ||||||
| 
 |  | ||||||
| 				err = check_return_code(env); | 				err = check_return_code(env); | ||||||
| 				if (err) | 				if (err) | ||||||
| 					return err; | 					return err; | ||||||
|  | @ -8829,12 +8846,14 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) | ||||||
| 			convert_ctx_access = bpf_xdp_sock_convert_ctx_access; | 			convert_ctx_access = bpf_xdp_sock_convert_ctx_access; | ||||||
| 			break; | 			break; | ||||||
| 		case PTR_TO_BTF_ID: | 		case PTR_TO_BTF_ID: | ||||||
| 			if (type == BPF_WRITE) { | 			if (type == BPF_READ) { | ||||||
|  | 				insn->code = BPF_LDX | BPF_PROBE_MEM | | ||||||
|  | 					BPF_SIZE((insn)->code); | ||||||
|  | 				env->prog->aux->num_exentries++; | ||||||
|  | 			} else if (env->prog->type != BPF_PROG_TYPE_STRUCT_OPS) { | ||||||
| 				verbose(env, "Writes through BTF pointers are not allowed\n"); | 				verbose(env, "Writes through BTF pointers are not allowed\n"); | ||||||
| 				return -EINVAL; | 				return -EINVAL; | ||||||
| 			} | 			} | ||||||
| 			insn->code = BPF_LDX | BPF_PROBE_MEM | BPF_SIZE((insn)->code); |  | ||||||
| 			env->prog->aux->num_exentries++; |  | ||||||
| 			continue; | 			continue; | ||||||
| 		default: | 		default: | ||||||
| 			continue; | 			continue; | ||||||
|  | @ -9502,6 +9521,58 @@ static void print_verification_stats(struct bpf_verifier_env *env) | ||||||
| 		env->peak_states, env->longest_mark_read_walk); | 		env->peak_states, env->longest_mark_read_walk); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int check_struct_ops_btf_id(struct bpf_verifier_env *env) | ||||||
|  | { | ||||||
|  | 	const struct btf_type *t, *func_proto; | ||||||
|  | 	const struct bpf_struct_ops *st_ops; | ||||||
|  | 	const struct btf_member *member; | ||||||
|  | 	struct bpf_prog *prog = env->prog; | ||||||
|  | 	u32 btf_id, member_idx; | ||||||
|  | 	const char *mname; | ||||||
|  | 
 | ||||||
|  | 	btf_id = prog->aux->attach_btf_id; | ||||||
|  | 	st_ops = bpf_struct_ops_find(btf_id); | ||||||
|  | 	if (!st_ops) { | ||||||
|  | 		verbose(env, "attach_btf_id %u is not a supported struct\n", | ||||||
|  | 			btf_id); | ||||||
|  | 		return -ENOTSUPP; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	t = st_ops->type; | ||||||
|  | 	member_idx = prog->expected_attach_type; | ||||||
|  | 	if (member_idx >= btf_type_vlen(t)) { | ||||||
|  | 		verbose(env, "attach to invalid member idx %u of struct %s\n", | ||||||
|  | 			member_idx, st_ops->name); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	member = &btf_type_member(t)[member_idx]; | ||||||
|  | 	mname = btf_name_by_offset(btf_vmlinux, member->name_off); | ||||||
|  | 	func_proto = btf_type_resolve_func_ptr(btf_vmlinux, member->type, | ||||||
|  | 					       NULL); | ||||||
|  | 	if (!func_proto) { | ||||||
|  | 		verbose(env, "attach to invalid member %s(@idx %u) of struct %s\n", | ||||||
|  | 			mname, member_idx, st_ops->name); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (st_ops->check_member) { | ||||||
|  | 		int err = st_ops->check_member(t, member); | ||||||
|  | 
 | ||||||
|  | 		if (err) { | ||||||
|  | 			verbose(env, "attach to unsupported member %s of struct %s\n", | ||||||
|  | 				mname, st_ops->name); | ||||||
|  | 			return err; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	prog->aux->attach_func_proto = func_proto; | ||||||
|  | 	prog->aux->attach_func_name = mname; | ||||||
|  | 	env->ops = st_ops->verifier_ops; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int check_attach_btf_id(struct bpf_verifier_env *env) | static int check_attach_btf_id(struct bpf_verifier_env *env) | ||||||
| { | { | ||||||
| 	struct bpf_prog *prog = env->prog; | 	struct bpf_prog *prog = env->prog; | ||||||
|  | @ -9517,6 +9588,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) | ||||||
| 	long addr; | 	long addr; | ||||||
| 	u64 key; | 	u64 key; | ||||||
| 
 | 
 | ||||||
|  | 	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS) | ||||||
|  | 		return check_struct_ops_btf_id(env); | ||||||
|  | 
 | ||||||
| 	if (prog->type != BPF_PROG_TYPE_TRACING) | 	if (prog->type != BPF_PROG_TYPE_TRACING) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Martin KaFai Lau
						Martin KaFai Lau