diff options
Diffstat (limited to 'kernel/bpf/btf.c')
| -rw-r--r-- | kernel/bpf/btf.c | 566 | 
1 files changed, 456 insertions, 110 deletions
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 596471189176..90c4a32d89ff 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -19,6 +19,7 @@  #include <linux/bpf_verifier.h>  #include <linux/btf.h>  #include <linux/btf_ids.h> +#include <linux/bpf.h>  #include <linux/bpf_lsm.h>  #include <linux/skmsg.h>  #include <linux/perf_event.h> @@ -241,6 +242,12 @@ struct btf_id_dtor_kfunc_tab {  	struct btf_id_dtor_kfunc dtors[];  }; +struct btf_struct_ops_tab { +	u32 cnt; +	u32 capacity; +	struct bpf_struct_ops_desc ops[]; +}; +  struct btf {  	void *data;  	struct btf_type **types; @@ -258,6 +265,7 @@ struct btf {  	struct btf_kfunc_set_tab *kfunc_set_tab;  	struct btf_id_dtor_kfunc_tab *dtor_kfunc_tab;  	struct btf_struct_metas *struct_meta_tab; +	struct btf_struct_ops_tab *struct_ops_tab;  	/* split BTF support */  	struct btf *base_btf; @@ -801,9 +809,23 @@ static bool btf_name_valid_identifier(const struct btf *btf, u32 offset)  	return __btf_name_valid(btf, offset);  } +/* Allow any printable character in DATASEC names */  static bool btf_name_valid_section(const struct btf *btf, u32 offset)  { -	return __btf_name_valid(btf, offset); +	/* offset must be valid */ +	const char *src = btf_str_by_offset(btf, offset); +	const char *src_limit; + +	/* set a limit on identifier length */ +	src_limit = src + KSYM_NAME_LEN; +	src++; +	while (*src && src < src_limit) { +		if (!isprint(*src)) +			return false; +		src++; +	} + +	return !*src;  }  static const char *__btf_name_by_offset(const struct btf *btf, u32 offset) @@ -1688,11 +1710,27 @@ static void btf_free_struct_meta_tab(struct btf *btf)  	btf->struct_meta_tab = NULL;  } +static void btf_free_struct_ops_tab(struct btf *btf) +{ +	struct btf_struct_ops_tab *tab = btf->struct_ops_tab; +	u32 i; + +	if (!tab) +		return; + +	for (i = 0; i < tab->cnt; i++) +		bpf_struct_ops_desc_release(&tab->ops[i]); + +	kfree(tab); +	btf->struct_ops_tab = NULL; +} +  static void btf_free(struct btf *btf)  {  	btf_free_struct_meta_tab(btf);  	btf_free_dtor_kfunc_tab(btf);  	btf_free_kfunc_set_tab(btf); +	btf_free_struct_ops_tab(btf);  	kvfree(btf->types);  	kvfree(btf->resolved_sizes);  	kvfree(btf->resolved_ids); @@ -1707,6 +1745,11 @@ static void btf_free_rcu(struct rcu_head *rcu)  	btf_free(btf);  } +const char *btf_get_name(const struct btf *btf) +{ +	return btf->name; +} +  void btf_get(struct btf *btf)  {  	refcount_inc(&btf->refcnt); @@ -3310,30 +3353,48 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t,  	return BTF_FIELD_FOUND;  } -const char *btf_find_decl_tag_value(const struct btf *btf, const struct btf_type *pt, -				    int comp_idx, const char *tag_key) +int btf_find_next_decl_tag(const struct btf *btf, const struct btf_type *pt, +			   int comp_idx, const char *tag_key, int last_id)  { -	const char *value = NULL; -	int i; +	int len = strlen(tag_key); +	int i, n; -	for (i = 1; i < btf_nr_types(btf); i++) { +	for (i = last_id + 1, n = btf_nr_types(btf); i < n; i++) {  		const struct btf_type *t = btf_type_by_id(btf, i); -		int len = strlen(tag_key);  		if (!btf_type_is_decl_tag(t))  			continue; -		if (pt != btf_type_by_id(btf, t->type) || -		    btf_type_decl_tag(t)->component_idx != comp_idx) +		if (pt != btf_type_by_id(btf, t->type)) +			continue; +		if (btf_type_decl_tag(t)->component_idx != comp_idx)  			continue;  		if (strncmp(__btf_name_by_offset(btf, t->name_off), tag_key, len))  			continue; -		/* Prevent duplicate entries for same type */ -		if (value) -			return ERR_PTR(-EEXIST); -		value = __btf_name_by_offset(btf, t->name_off) + len; +		return i;  	} -	if (!value) -		return ERR_PTR(-ENOENT); +	return -ENOENT; +} + +const char *btf_find_decl_tag_value(const struct btf *btf, const struct btf_type *pt, +				    int comp_idx, const char *tag_key) +{ +	const char *value = NULL; +	const struct btf_type *t; +	int len, id; + +	id = btf_find_next_decl_tag(btf, pt, comp_idx, tag_key, 0); +	if (id < 0) +		return ERR_PTR(id); + +	t = btf_type_by_id(btf, id); +	len = strlen(tag_key); +	value = __btf_name_by_offset(btf, t->name_off) + len; + +	/* Prevent duplicate entries for same type */ +	id = btf_find_next_decl_tag(btf, pt, comp_idx, tag_key, id); +	if (id >= 0) +		return ERR_PTR(-EEXIST); +  	return value;  } @@ -5647,15 +5708,29 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)  	return ctx_type->type;  } -const struct btf_type * -btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, -		      const struct btf_type *t, enum bpf_prog_type prog_type, -		      int arg) +bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, +			  const struct btf_type *t, enum bpf_prog_type prog_type, +			  int arg)  {  	const struct btf_type *ctx_type;  	const char *tname, *ctx_tname;  	t = btf_type_by_id(btf, t->type); + +	/* KPROBE programs allow bpf_user_pt_regs_t typedef, which we need to +	 * check before we skip all the typedef below. +	 */ +	if (prog_type == BPF_PROG_TYPE_KPROBE) { +		while (btf_type_is_modifier(t) && !btf_type_is_typedef(t)) +			t = btf_type_by_id(btf, t->type); + +		if (btf_type_is_typedef(t)) { +			tname = btf_name_by_offset(btf, t->name_off); +			if (tname && strcmp(tname, "bpf_user_pt_regs_t") == 0) +				return true; +		} +	} +  	while (btf_type_is_modifier(t))  		t = btf_type_by_id(btf, t->type);  	if (!btf_type_is_struct(t)) { @@ -5664,27 +5739,30 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,  		 * is not supported yet.  		 * BPF_PROG_TYPE_RAW_TRACEPOINT is fine.  		 */ -		return NULL; +		return false;  	}  	tname = btf_name_by_offset(btf, t->name_off);  	if (!tname) {  		bpf_log(log, "arg#%d struct doesn't have a name\n", arg); -		return NULL; +		return false;  	}  	ctx_type = find_canonical_prog_ctx_type(prog_type);  	if (!ctx_type) {  		bpf_log(log, "btf_vmlinux is malformed\n");  		/* should not happen */ -		return NULL; +		return false;  	}  again:  	ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);  	if (!ctx_tname) {  		/* should not happen */  		bpf_log(log, "Please fix kernel include/linux/bpf_types.h\n"); -		return NULL; +		return false;  	} +	/* program types without named context types work only with arg:ctx tag */ +	if (ctx_tname[0] == '\0') +		return false;  	/* only compare that prog's ctx type name is the same as  	 * kernel expects. No need to compare field by field.  	 * It's ok for bpf prog to do: @@ -5693,20 +5771,20 @@ again:  	 * { // no fields of skb are ever used }  	 */  	if (strcmp(ctx_tname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0) -		return ctx_type; +		return true;  	if (strcmp(ctx_tname, "xdp_md") == 0 && strcmp(tname, "xdp_buff") == 0) -		return ctx_type; +		return true;  	if (strcmp(ctx_tname, tname)) {  		/* bpf_user_pt_regs_t is a typedef, so resolve it to  		 * underlying struct and check name again  		 */  		if (!btf_type_is_modifier(ctx_type)) -			return NULL; +			return false;  		while (btf_type_is_modifier(ctx_type))  			ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);  		goto again;  	} -	return ctx_type; +	return true;  }  /* forward declarations for arch-specific underlying types of @@ -5858,7 +5936,7 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,  				     enum bpf_prog_type prog_type,  				     int arg)  { -	if (!btf_get_prog_ctx_type(log, btf, t, prog_type, arg)) +	if (!btf_is_prog_ctx_type(log, btf, t, prog_type, arg))  		return -ENOENT;  	return find_kern_ctx_type_id(prog_type);  } @@ -5933,8 +6011,6 @@ struct btf *btf_parse_vmlinux(void)  	/* btf_parse_vmlinux() runs under bpf_verifier_lock */  	bpf_ctx_convert.t = btf_type_by_id(btf, bpf_ctx_convert_btf_id[0]); -	bpf_struct_ops_init(btf, log); -  	refcount_set(&btf->refcnt, 1);  	err = btf_alloc_id(btf); @@ -6092,6 +6168,26 @@ static bool prog_args_trusted(const struct bpf_prog *prog)  	}  } +int btf_ctx_arg_offset(const struct btf *btf, const struct btf_type *func_proto, +		       u32 arg_no) +{ +	const struct btf_param *args; +	const struct btf_type *t; +	int off = 0, i; +	u32 sz; + +	args = btf_params(func_proto); +	for (i = 0; i < arg_no; i++) { +		t = btf_type_by_id(btf, args[i].type); +		t = btf_resolve_size(btf, t, &sz); +		if (IS_ERR(t)) +			return PTR_ERR(t); +		off += roundup(sz, 8); +	} + +	return off; +} +  bool btf_ctx_access(int off, int size, enum bpf_access_type type,  		    const struct bpf_prog *prog,  		    struct bpf_insn_access_aux *info) @@ -6228,7 +6324,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,  			}  			info->reg_type = ctx_arg_info->reg_type; -			info->btf = btf_vmlinux; +			info->btf = ctx_arg_info->btf ? : btf_vmlinux;  			info->btf_id = ctx_arg_info->btf_id;  			return true;  		} @@ -6284,6 +6380,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,  		__btf_name_by_offset(btf, t->name_off));  	return true;  } +EXPORT_SYMBOL_GPL(btf_ctx_access);  enum bpf_struct_walk_result {  	/* < 0 error */ @@ -6946,6 +7043,81 @@ static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t)  	return false;  } +struct bpf_cand_cache { +	const char *name; +	u32 name_len; +	u16 kind; +	u16 cnt; +	struct { +		const struct btf *btf; +		u32 id; +	} cands[]; +}; + +static DEFINE_MUTEX(cand_cache_mutex); + +static struct bpf_cand_cache * +bpf_core_find_cands(struct bpf_core_ctx *ctx, u32 local_type_id); + +static int btf_get_ptr_to_btf_id(struct bpf_verifier_log *log, int arg_idx, +				 const struct btf *btf, const struct btf_type *t) +{ +	struct bpf_cand_cache *cc; +	struct bpf_core_ctx ctx = { +		.btf = btf, +		.log = log, +	}; +	u32 kern_type_id, type_id; +	int err = 0; + +	/* skip PTR and modifiers */ +	type_id = t->type; +	t = btf_type_by_id(btf, t->type); +	while (btf_type_is_modifier(t)) { +		type_id = t->type; +		t = btf_type_by_id(btf, t->type); +	} + +	mutex_lock(&cand_cache_mutex); +	cc = bpf_core_find_cands(&ctx, type_id); +	if (IS_ERR(cc)) { +		err = PTR_ERR(cc); +		bpf_log(log, "arg#%d reference type('%s %s') candidate matching error: %d\n", +			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off), +			err); +		goto cand_cache_unlock; +	} +	if (cc->cnt != 1) { +		bpf_log(log, "arg#%d reference type('%s %s') %s\n", +			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off), +			cc->cnt == 0 ? "has no matches" : "is ambiguous"); +		err = cc->cnt == 0 ? -ENOENT : -ESRCH; +		goto cand_cache_unlock; +	} +	if (btf_is_module(cc->cands[0].btf)) { +		bpf_log(log, "arg#%d reference type('%s %s') points to kernel module type (unsupported)\n", +			arg_idx, btf_type_str(t), __btf_name_by_offset(btf, t->name_off)); +		err = -EOPNOTSUPP; +		goto cand_cache_unlock; +	} +	kern_type_id = cc->cands[0].id; + +cand_cache_unlock: +	mutex_unlock(&cand_cache_mutex); +	if (err) +		return err; + +	return kern_type_id; +} + +enum btf_arg_tag { +	ARG_TAG_CTX	 = BIT_ULL(0), +	ARG_TAG_NONNULL  = BIT_ULL(1), +	ARG_TAG_TRUSTED  = BIT_ULL(2), +	ARG_TAG_NULLABLE = BIT_ULL(3), +	ARG_TAG_ARENA	 = BIT_ULL(4), +}; +  /* Process BTF of a function to produce high-level expectation of function   * arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information   * is cached in subprog info for reuse. @@ -7009,6 +7181,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)  	args = (const struct btf_param *)(t + 1);  	nargs = btf_type_vlen(t);  	if (nargs > MAX_BPF_FUNC_REG_ARGS) { +		if (!is_global) +			return -EINVAL;  		bpf_log(log, "Global function %s() with %d > %d args. Buggy compiler.\n",  			tname, nargs, MAX_BPF_FUNC_REG_ARGS);  		return -EINVAL; @@ -7018,6 +7192,8 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)  	while (btf_type_is_modifier(t))  		t = btf_type_by_id(btf, t->type);  	if (!btf_type_is_int(t) && !btf_is_any_enum(t)) { +		if (!is_global) +			return -EINVAL;  		bpf_log(log,  			"Global function %s() doesn't return scalar. Only those are supported.\n",  			tname); @@ -7027,92 +7203,134 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)  	 * Only PTR_TO_CTX and SCALAR are supported atm.  	 */  	for (i = 0; i < nargs; i++) { -		bool is_nonnull = false; -		const char *tag; +		u32 tags = 0; +		int id = 0; -		t = btf_type_by_id(btf, args[i].type); - -		tag = btf_find_decl_tag_value(btf, fn_t, i, "arg:"); -		if (IS_ERR(tag) && PTR_ERR(tag) == -ENOENT) { -			tag = NULL; -		} else if (IS_ERR(tag)) { -			bpf_log(log, "arg#%d type's tag fetching failure: %ld\n", i, PTR_ERR(tag)); -			return PTR_ERR(tag); -		}  		/* 'arg:<tag>' decl_tag takes precedence over derivation of  		 * register type from BTF type itself  		 */ -		if (tag) { +		while ((id = btf_find_next_decl_tag(btf, fn_t, i, "arg:", id)) > 0) { +			const struct btf_type *tag_t = btf_type_by_id(btf, id); +			const char *tag = __btf_name_by_offset(btf, tag_t->name_off) + 4; +  			/* disallow arg tags in static subprogs */  			if (!is_global) {  				bpf_log(log, "arg#%d type tag is not supported in static functions\n", i);  				return -EOPNOTSUPP;  			} +  			if (strcmp(tag, "ctx") == 0) { -				sub->args[i].arg_type = ARG_PTR_TO_CTX; -				continue; +				tags |= ARG_TAG_CTX; +			} else if (strcmp(tag, "trusted") == 0) { +				tags |= ARG_TAG_TRUSTED; +			} else if (strcmp(tag, "nonnull") == 0) { +				tags |= ARG_TAG_NONNULL; +			} else if (strcmp(tag, "nullable") == 0) { +				tags |= ARG_TAG_NULLABLE; +			} else if (strcmp(tag, "arena") == 0) { +				tags |= ARG_TAG_ARENA; +			} else { +				bpf_log(log, "arg#%d has unsupported set of tags\n", i); +				return -EOPNOTSUPP;  			} -			if (strcmp(tag, "nonnull") == 0) -				is_nonnull = true; +		} +		if (id != -ENOENT) { +			bpf_log(log, "arg#%d type tag fetching failure: %d\n", i, id); +			return id;  		} +		t = btf_type_by_id(btf, args[i].type);  		while (btf_type_is_modifier(t))  			t = btf_type_by_id(btf, t->type); -		if (btf_type_is_int(t) || btf_is_any_enum(t)) { -			sub->args[i].arg_type = ARG_ANYTHING; -			continue; -		} -		if (btf_type_is_ptr(t) && btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { +		if (!btf_type_is_ptr(t)) +			goto skip_pointer; + +		if ((tags & ARG_TAG_CTX) || btf_is_prog_ctx_type(log, btf, t, prog_type, i)) { +			if (tags & ~ARG_TAG_CTX) { +				bpf_log(log, "arg#%d has invalid combination of tags\n", i); +				return -EINVAL; +			} +			if ((tags & ARG_TAG_CTX) && +			    btf_validate_prog_ctx_type(log, btf, t, i, prog_type, +						       prog->expected_attach_type)) +				return -EINVAL;  			sub->args[i].arg_type = ARG_PTR_TO_CTX;  			continue;  		} -		if (btf_type_is_ptr(t) && btf_is_dynptr_ptr(btf, t)) { +		if (btf_is_dynptr_ptr(btf, t)) { +			if (tags) { +				bpf_log(log, "arg#%d has invalid combination of tags\n", i); +				return -EINVAL; +			}  			sub->args[i].arg_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY;  			continue;  		} -		if (is_global && btf_type_is_ptr(t)) { +		if (tags & ARG_TAG_TRUSTED) { +			int kern_type_id; + +			if (tags & ARG_TAG_NONNULL) { +				bpf_log(log, "arg#%d has invalid combination of tags\n", i); +				return -EINVAL; +			} + +			kern_type_id = btf_get_ptr_to_btf_id(log, i, btf, t); +			if (kern_type_id < 0) +				return kern_type_id; + +			sub->args[i].arg_type = ARG_PTR_TO_BTF_ID | PTR_TRUSTED; +			if (tags & ARG_TAG_NULLABLE) +				sub->args[i].arg_type |= PTR_MAYBE_NULL; +			sub->args[i].btf_id = kern_type_id; +			continue; +		} +		if (tags & ARG_TAG_ARENA) { +			if (tags & ~ARG_TAG_ARENA) { +				bpf_log(log, "arg#%d arena cannot be combined with any other tags\n", i); +				return -EINVAL; +			} +			sub->args[i].arg_type = ARG_PTR_TO_ARENA; +			continue; +		} +		if (is_global) { /* generic user data pointer */  			u32 mem_size; +			if (tags & ARG_TAG_NULLABLE) { +				bpf_log(log, "arg#%d has invalid combination of tags\n", i); +				return -EINVAL; +			} +  			t = btf_type_skip_modifiers(btf, t->type, NULL);  			ref_t = btf_resolve_size(btf, t, &mem_size);  			if (IS_ERR(ref_t)) { -				bpf_log(log, -				    "arg#%d reference type('%s %s') size cannot be determined: %ld\n", -				    i, btf_type_str(t), btf_name_by_offset(btf, t->name_off), +				bpf_log(log, "arg#%d reference type('%s %s') size cannot be determined: %ld\n", +					i, btf_type_str(t), btf_name_by_offset(btf, t->name_off),  					PTR_ERR(ref_t));  				return -EINVAL;  			} -			sub->args[i].arg_type = is_nonnull ? ARG_PTR_TO_MEM : ARG_PTR_TO_MEM_OR_NULL; +			sub->args[i].arg_type = ARG_PTR_TO_MEM | PTR_MAYBE_NULL; +			if (tags & ARG_TAG_NONNULL) +				sub->args[i].arg_type &= ~PTR_MAYBE_NULL;  			sub->args[i].mem_size = mem_size;  			continue;  		} -		if (is_nonnull) { -			bpf_log(log, "arg#%d marked as non-null, but is not a pointer type\n", i); + +skip_pointer: +		if (tags) { +			bpf_log(log, "arg#%d has pointer tag, but is not a pointer type\n", i);  			return -EINVAL;  		} +		if (btf_type_is_int(t) || btf_is_any_enum(t)) { +			sub->args[i].arg_type = ARG_ANYTHING; +			continue; +		} +		if (!is_global) +			return -EINVAL;  		bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n",  			i, btf_type_str(t), tname);  		return -EINVAL;  	} -	for (i = 0; i < nargs; i++) { -		const char *tag; - -		if (sub->args[i].arg_type != ARG_PTR_TO_CTX) -			continue; - -		/* check if arg has "arg:ctx" tag */ -		t = btf_type_by_id(btf, args[i].type); -		tag = btf_find_decl_tag_value(btf, fn_t, i, "arg:"); -		if (IS_ERR_OR_NULL(tag) || strcmp(tag, "ctx") != 0) -			continue; - -		if (btf_validate_prog_ctx_type(log, btf, t, i, prog_type, -					       prog->expected_attach_type)) -			return -EINVAL; -	} -  	sub->arg_cnt = nargs;  	sub->args_cached = true; @@ -7589,6 +7807,17 @@ static struct btf *btf_get_module_btf(const struct module *module)  	return btf;  } +static int check_btf_kconfigs(const struct module *module, const char *feature) +{ +	if (!module && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) { +		pr_err("missing vmlinux BTF, cannot register %s\n", feature); +		return -ENOENT; +	} +	if (module && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) +		pr_warn("missing module BTF, cannot register %s\n", feature); +	return 0; +} +  BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)  {  	struct btf *btf = NULL; @@ -7949,15 +8178,8 @@ static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,  	int ret, i;  	btf = btf_get_module_btf(kset->owner); -	if (!btf) { -		if (!kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) { -			pr_err("missing vmlinux BTF, cannot register kfuncs\n"); -			return -ENOENT; -		} -		if (kset->owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) -			pr_warn("missing module BTF, cannot register kfuncs\n"); -		return 0; -	} +	if (!btf) +		return check_btf_kconfigs(kset->owner, "kfunc");  	if (IS_ERR(btf))  		return PTR_ERR(btf); @@ -7981,6 +8203,14 @@ int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,  {  	enum btf_kfunc_hook hook; +	/* All kfuncs need to be tagged as such in BTF. +	 * WARN() for initcall registrations that do not check errors. +	 */ +	if (!(kset->set->flags & BTF_SET8_KFUNCS)) { +		WARN_ON(!kset->owner); +		return -EINVAL; +	} +  	hook = bpf_prog_type_to_kfunc_hook(prog_type);  	return __register_btf_kfunc_id_set(hook, kset);  } @@ -8057,17 +8287,8 @@ int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_c  	int ret;  	btf = btf_get_module_btf(owner); -	if (!btf) { -		if (!owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) { -			pr_err("missing vmlinux BTF, cannot register dtor kfuncs\n"); -			return -ENOENT; -		} -		if (owner && IS_ENABLED(CONFIG_DEBUG_INFO_BTF_MODULES)) { -			pr_err("missing module BTF, cannot register dtor kfuncs\n"); -			return -ENOENT; -		} -		return 0; -	} +	if (!btf) +		return check_btf_kconfigs(owner, "dtor kfuncs");  	if (IS_ERR(btf))  		return PTR_ERR(btf); @@ -8182,17 +8403,6 @@ size_t bpf_core_essential_name_len(const char *name)  	return n;  } -struct bpf_cand_cache { -	const char *name; -	u32 name_len; -	u16 kind; -	u16 cnt; -	struct { -		const struct btf *btf; -		u32 id; -	} cands[]; -}; -  static void bpf_free_cands(struct bpf_cand_cache *cands)  {  	if (!cands->cnt) @@ -8213,8 +8423,6 @@ static struct bpf_cand_cache *vmlinux_cand_cache[VMLINUX_CAND_CACHE_SIZE];  #define MODULE_CAND_CACHE_SIZE 31  static struct bpf_cand_cache *module_cand_cache[MODULE_CAND_CACHE_SIZE]; -static DEFINE_MUTEX(cand_cache_mutex); -  static void __print_cand_cache(struct bpf_verifier_log *log,  			       struct bpf_cand_cache **cache,  			       int cache_size) @@ -8645,3 +8853,141 @@ bool btf_type_ids_nocast_alias(struct bpf_verifier_log *log,  	return !strncmp(reg_name, arg_name, cmp_len);  } + +#ifdef CONFIG_BPF_JIT +static int +btf_add_struct_ops(struct btf *btf, struct bpf_struct_ops *st_ops, +		   struct bpf_verifier_log *log) +{ +	struct btf_struct_ops_tab *tab, *new_tab; +	int i, err; + +	tab = btf->struct_ops_tab; +	if (!tab) { +		tab = kzalloc(offsetof(struct btf_struct_ops_tab, ops[4]), +			      GFP_KERNEL); +		if (!tab) +			return -ENOMEM; +		tab->capacity = 4; +		btf->struct_ops_tab = tab; +	} + +	for (i = 0; i < tab->cnt; i++) +		if (tab->ops[i].st_ops == st_ops) +			return -EEXIST; + +	if (tab->cnt == tab->capacity) { +		new_tab = krealloc(tab, +				   offsetof(struct btf_struct_ops_tab, +					    ops[tab->capacity * 2]), +				   GFP_KERNEL); +		if (!new_tab) +			return -ENOMEM; +		tab = new_tab; +		tab->capacity *= 2; +		btf->struct_ops_tab = tab; +	} + +	tab->ops[btf->struct_ops_tab->cnt].st_ops = st_ops; + +	err = bpf_struct_ops_desc_init(&tab->ops[btf->struct_ops_tab->cnt], btf, log); +	if (err) +		return err; + +	btf->struct_ops_tab->cnt++; + +	return 0; +} + +const struct bpf_struct_ops_desc * +bpf_struct_ops_find_value(struct btf *btf, u32 value_id) +{ +	const struct bpf_struct_ops_desc *st_ops_list; +	unsigned int i; +	u32 cnt; + +	if (!value_id) +		return NULL; +	if (!btf->struct_ops_tab) +		return NULL; + +	cnt = btf->struct_ops_tab->cnt; +	st_ops_list = btf->struct_ops_tab->ops; +	for (i = 0; i < cnt; i++) { +		if (st_ops_list[i].value_id == value_id) +			return &st_ops_list[i]; +	} + +	return NULL; +} + +const struct bpf_struct_ops_desc * +bpf_struct_ops_find(struct btf *btf, u32 type_id) +{ +	const struct bpf_struct_ops_desc *st_ops_list; +	unsigned int i; +	u32 cnt; + +	if (!type_id) +		return NULL; +	if (!btf->struct_ops_tab) +		return NULL; + +	cnt = btf->struct_ops_tab->cnt; +	st_ops_list = btf->struct_ops_tab->ops; +	for (i = 0; i < cnt; i++) { +		if (st_ops_list[i].type_id == type_id) +			return &st_ops_list[i]; +	} + +	return NULL; +} + +int __register_bpf_struct_ops(struct bpf_struct_ops *st_ops) +{ +	struct bpf_verifier_log *log; +	struct btf *btf; +	int err = 0; + +	btf = btf_get_module_btf(st_ops->owner); +	if (!btf) +		return check_btf_kconfigs(st_ops->owner, "struct_ops"); +	if (IS_ERR(btf)) +		return PTR_ERR(btf); + +	log = kzalloc(sizeof(*log), GFP_KERNEL | __GFP_NOWARN); +	if (!log) { +		err = -ENOMEM; +		goto errout; +	} + +	log->level = BPF_LOG_KERNEL; + +	err = btf_add_struct_ops(btf, st_ops, log); + +errout: +	kfree(log); +	btf_put(btf); + +	return err; +} +EXPORT_SYMBOL_GPL(__register_bpf_struct_ops); +#endif + +bool btf_param_match_suffix(const struct btf *btf, +			    const struct btf_param *arg, +			    const char *suffix) +{ +	int suffix_len = strlen(suffix), len; +	const char *param_name; + +	/* In the future, this can be ported to use BTF tagging */ +	param_name = btf_name_by_offset(btf, arg->name_off); +	if (str_is_empty(param_name)) +		return false; +	len = strlen(param_name); +	if (len <= suffix_len) +		return false; +	param_name += len - suffix_len; +	return !strncmp(param_name, suffix, suffix_len); +}  |