diff options
| -rw-r--r-- | include/linux/bpf.h | 1 | ||||
| -rw-r--r-- | include/uapi/linux/bpf.h | 6 | ||||
| -rw-r--r-- | kernel/bpf/syscall.c | 31 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 6 | ||||
| -rw-r--r-- | tools/include/uapi/linux/bpf.h | 6 | 
5 files changed, 46 insertions, 4 deletions
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d75902074bd1..c6a6ee1b2938 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -329,6 +329,7 @@ struct bpf_map {  	atomic64_t sleepable_refcnt;  	s64 __percpu *elem_count;  	u64 cookie; /* write-once */ +	char *excl_prog_sha;  };  static inline const char *btf_field_type_name(enum btf_field_type type) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 233de8677382..57687b2e1c47 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1522,6 +1522,12 @@ union bpf_attr {  		 * If provided, map_flags should have BPF_F_TOKEN_FD flag set.  		 */  		__s32	map_token_fd; + +		/* Hash of the program that has exclusive access to the map. +		 */ +		__aligned_u64 excl_prog_hash; +		/* Size of the passed excl_prog_hash. */ +		__u32 excl_prog_hash_size;  	};  	struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 3f178a0f8eb1..c8ef91acfe98 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -860,6 +860,7 @@ static void bpf_map_free(struct bpf_map *map)  	 * the free of values or special fields allocated from bpf memory  	 * allocator.  	 */ +	kfree(map->excl_prog_sha);  	migrate_disable();  	map->ops->map_free(map);  	migrate_enable(); @@ -1338,9 +1339,9 @@ static bool bpf_net_capable(void)  	return capable(CAP_NET_ADMIN) || capable(CAP_SYS_ADMIN);  } -#define BPF_MAP_CREATE_LAST_FIELD map_token_fd +#define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size  /* called via syscall */ -static int map_create(union bpf_attr *attr, bool kernel) +static int map_create(union bpf_attr *attr, bpfptr_t uattr)  {  	const struct bpf_map_ops *ops;  	struct bpf_token *token = NULL; @@ -1534,7 +1535,29 @@ static int map_create(union bpf_attr *attr, bool kernel)  			attr->btf_vmlinux_value_type_id;  	} -	err = security_bpf_map_create(map, attr, token, kernel); +	if (attr->excl_prog_hash) { +		bpfptr_t uprog_hash = make_bpfptr(attr->excl_prog_hash, uattr.is_kernel); + +		if (attr->excl_prog_hash_size != SHA256_DIGEST_SIZE) { +			err = -EINVAL; +			goto free_map; +		} + +		map->excl_prog_sha = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); +		if (!map->excl_prog_sha) { +			err = -ENOMEM; +			goto free_map; +		} + +		if (copy_from_bpfptr(map->excl_prog_sha, uprog_hash, SHA256_DIGEST_SIZE)) { +			err = -EFAULT; +			goto free_map; +		} +	} else if (attr->excl_prog_hash_size) { +		return -EINVAL; +	} + +	err = security_bpf_map_create(map, attr, token, uattr.is_kernel);  	if (err)  		goto free_map_sec; @@ -6008,7 +6031,7 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size)  	switch (cmd) {  	case BPF_MAP_CREATE: -		err = map_create(&attr, uattr.is_kernel); +		err = map_create(&attr, uattr);  		break;  	case BPF_MAP_LOOKUP_ELEM:  		err = map_lookup_elem(&attr); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6625570ac23d..aef6b266f08d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -20407,6 +20407,12 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env,  {  	enum bpf_prog_type prog_type = resolve_prog_type(prog); +	if (map->excl_prog_sha && +	    memcmp(map->excl_prog_sha, prog->digest, SHA256_DIGEST_SIZE)) { +		verbose(env, "program's hash doesn't match map's excl_prog_hash\n"); +		return -EACCES; +	} +  	if (btf_record_has_field(map->record, BPF_LIST_HEAD) ||  	    btf_record_has_field(map->record, BPF_RB_ROOT)) {  		if (is_tracing_prog_type(prog_type)) { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 233de8677382..57687b2e1c47 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1522,6 +1522,12 @@ union bpf_attr {  		 * If provided, map_flags should have BPF_F_TOKEN_FD flag set.  		 */  		__s32	map_token_fd; + +		/* Hash of the program that has exclusive access to the map. +		 */ +		__aligned_u64 excl_prog_hash; +		/* Size of the passed excl_prog_hash. */ +		__u32 excl_prog_hash_size;  	};  	struct { /* anonymous struct used by BPF_MAP_*_ELEM and BPF_MAP_FREEZE commands */  | 
