diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2023-04-03 21:50:24 -0700 | 
|---|---|---|
| committer | Andrii Nakryiko <andrii@kernel.org> | 2023-04-04 16:57:14 -0700 | 
| commit | 63260df1396578226ac3134cf7f764690002e70e (patch) | |
| tree | c7b94a6b73081b69775b8c8d5c6ad1c52e02bdae /kernel | |
| parent | b7e852a9ec96635168c04204fb7cf1f7390b9a8c (diff) | |
bpf: Refactor btf_nested_type_is_trusted().
btf_nested_type_is_trusted() tries to find a struct member at corresponding offset.
It works for flat structures and falls apart in more complex structs with nested structs.
The offset->member search is already performed by btf_struct_walk() including nested structs.
Reuse this work and pass {field name, field btf id} into btf_nested_type_is_trusted()
instead of offset to make BTF_TYPE_SAFE*() logic more robust.
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: David Vernet <void@manifault.com>
Link: https://lore.kernel.org/bpf/20230404045029.82870-4-alexei.starovoitov@gmail.com
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/btf.c | 44 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 23 | 
2 files changed, 29 insertions, 38 deletions
| diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index b7e5a5510b91..593c45a294d0 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6166,7 +6166,8 @@ enum bpf_struct_walk_result {  static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,  			   const struct btf_type *t, int off, int size, -			   u32 *next_btf_id, enum bpf_type_flag *flag) +			   u32 *next_btf_id, enum bpf_type_flag *flag, +			   const char **field_name)  {  	u32 i, moff, mtrue_end, msize = 0, total_nelems = 0;  	const struct btf_type *mtype, *elem_type = NULL; @@ -6395,6 +6396,8 @@ error:  			if (btf_type_is_struct(stype)) {  				*next_btf_id = id;  				*flag |= tmp_flag; +				if (field_name) +					*field_name = mname;  				return WALK_PTR;  			}  		} @@ -6421,7 +6424,8 @@ error:  int btf_struct_access(struct bpf_verifier_log *log,  		      const struct bpf_reg_state *reg,  		      int off, int size, enum bpf_access_type atype __maybe_unused, -		      u32 *next_btf_id, enum bpf_type_flag *flag) +		      u32 *next_btf_id, enum bpf_type_flag *flag, +		      const char **field_name)  {  	const struct btf *btf = reg->btf;  	enum bpf_type_flag tmp_flag = 0; @@ -6453,7 +6457,7 @@ int btf_struct_access(struct bpf_verifier_log *log,  	t = btf_type_by_id(btf, id);  	do { -		err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag); +		err = btf_struct_walk(log, btf, t, off, size, &id, &tmp_flag, field_name);  		switch (err) {  		case WALK_PTR: @@ -6528,7 +6532,7 @@ again:  	type = btf_type_by_id(btf, id);  	if (!type)  		return false; -	err = btf_struct_walk(log, btf, type, off, 1, &id, &flag); +	err = btf_struct_walk(log, btf, type, off, 1, &id, &flag, NULL);  	if (err != WALK_STRUCT)  		return false; @@ -8488,16 +8492,15 @@ out:  bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,  				const struct bpf_reg_state *reg, -				int off, const char *suffix) +				const char *field_name, u32 btf_id, const char *suffix)  {  	struct btf *btf = reg->btf;  	const struct btf_type *walk_type, *safe_type;  	const char *tname;  	char safe_tname[64];  	long ret, safe_id; -	const struct btf_member *member, *m_walk = NULL; +	const struct btf_member *member;  	u32 i; -	const char *walk_name;  	walk_type = btf_type_by_id(btf, reg->btf_id);  	if (!walk_type) @@ -8517,30 +8520,17 @@ bool btf_nested_type_is_trusted(struct bpf_verifier_log *log,  	if (!safe_type)  		return false; -	for_each_member(i, walk_type, member) { -		u32 moff; - -		/* We're looking for the PTR_TO_BTF_ID member in the struct -		 * type we're walking which matches the specified offset. -		 * Below, we'll iterate over the fields in the safe variant of -		 * the struct and see if any of them has a matching type / -		 * name. -		 */ -		moff = __btf_member_bit_offset(walk_type, member) / 8; -		if (off == moff) { -			m_walk = member; -			break; -		} -	} -	if (m_walk == NULL) -		return false; - -	walk_name = __btf_name_by_offset(btf, m_walk->name_off);  	for_each_member(i, safe_type, member) {  		const char *m_name = __btf_name_by_offset(btf, member->name_off); +		const struct btf_type *mtype = btf_type_by_id(btf, member->type); +		u32 id; + +		if (!btf_type_is_ptr(mtype)) +			continue; +		btf_type_skip_modifiers(btf, mtype->type, &id);  		/* If we match on both type and name, the field is considered trusted. */ -		if (m_walk->type == member->type && !strcmp(walk_name, m_name)) +		if (btf_id == id && !strcmp(field_name, m_name))  			return true;  	} diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 5ca520e5eddf..2cd2e0b725cd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5400,12 +5400,12 @@ BTF_TYPE_SAFE_RCU(struct css_set) {  /* full trusted: these fields are trusted even outside of RCU CS and never NULL */  BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta) { -	__bpf_md_ptr(struct seq_file *, seq); +	struct seq_file *seq;  };  BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task) { -	__bpf_md_ptr(struct bpf_iter_meta *, meta); -	__bpf_md_ptr(struct task_struct *, task); +	struct bpf_iter_meta *meta; +	struct task_struct *task;  };  BTF_TYPE_SAFE_TRUSTED(struct linux_binprm) { @@ -5427,17 +5427,17 @@ BTF_TYPE_SAFE_TRUSTED(struct socket) {  static bool type_is_rcu(struct bpf_verifier_env *env,  			struct bpf_reg_state *reg, -			int off) +			const char *field_name, u32 btf_id)  {  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct task_struct));  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct css_set)); -	return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_rcu"); +	return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_rcu");  }  static bool type_is_trusted(struct bpf_verifier_env *env,  			    struct bpf_reg_state *reg, -			    int off) +			    const char *field_name, u32 btf_id)  {  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta));  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct bpf_iter__task)); @@ -5446,7 +5446,7 @@ static bool type_is_trusted(struct bpf_verifier_env *env,  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct dentry));  	BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED(struct socket)); -	return btf_nested_type_is_trusted(&env->log, reg, off, "__safe_trusted"); +	return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_trusted");  }  static int check_ptr_to_btf_access(struct bpf_verifier_env *env, @@ -5458,6 +5458,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,  	struct bpf_reg_state *reg = regs + regno;  	const struct btf_type *t = btf_type_by_id(reg->btf, reg->btf_id);  	const char *tname = btf_name_by_offset(reg->btf, t->name_off); +	const char *field_name = NULL;  	enum bpf_type_flag flag = 0;  	u32 btf_id = 0;  	int ret; @@ -5526,7 +5527,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,  			return -EFAULT;  		} -		ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag); +		ret = btf_struct_access(&env->log, reg, off, size, atype, &btf_id, &flag, &field_name);  	}  	if (ret < 0) @@ -5554,10 +5555,10 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env,  		 * A regular RCU-protected pointer with __rcu tag can also be deemed  		 * trusted if we are in an RCU CS. Such pointer can be NULL.  		 */ -		if (type_is_trusted(env, reg, off)) { +		if (type_is_trusted(env, reg, field_name, btf_id)) {  			flag |= PTR_TRUSTED;  		} else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) { -			if (type_is_rcu(env, reg, off)) { +			if (type_is_rcu(env, reg, field_name, btf_id)) {  				/* ignore __rcu tag and mark it MEM_RCU */  				flag |= MEM_RCU;  			} else if (flag & MEM_RCU) { @@ -5640,7 +5641,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,  	/* Simulate access to a PTR_TO_BTF_ID */  	memset(&map_reg, 0, sizeof(map_reg));  	mark_btf_ld_reg(env, &map_reg, 0, PTR_TO_BTF_ID, btf_vmlinux, *map->ops->map_btf_id, 0); -	ret = btf_struct_access(&env->log, &map_reg, off, size, atype, &btf_id, &flag); +	ret = btf_struct_access(&env->log, &map_reg, off, size, atype, &btf_id, &flag, NULL);  	if (ret < 0)  		return ret; | 
