diff options
Diffstat (limited to 'kernel/bpf/syscall.c')
| -rw-r--r-- | kernel/bpf/syscall.c | 148 | 
1 files changed, 134 insertions, 14 deletions
| diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8339d81cba1d..ccb93277aae2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -30,7 +30,6 @@  #include <linux/cred.h>  #include <linux/timekeeping.h>  #include <linux/ctype.h> -#include <linux/btf.h>  #include <linux/nospec.h>  #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \ @@ -652,6 +651,17 @@ int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value)  	return -ENOTSUPP;  } +static void *__bpf_copy_key(void __user *ukey, u64 key_size) +{ +	if (key_size) +		return memdup_user(ukey, key_size); + +	if (ukey) +		return ERR_PTR(-EINVAL); + +	return NULL; +} +  /* last field in 'union bpf_attr' used by this command */  #define BPF_MAP_LOOKUP_ELEM_LAST_FIELD value @@ -679,7 +689,7 @@ static int map_lookup_elem(union bpf_attr *attr)  		goto err_put;  	} -	key = memdup_user(ukey, map->key_size); +	key = __bpf_copy_key(ukey, map->key_size);  	if (IS_ERR(key)) {  		err = PTR_ERR(key);  		goto err_put; @@ -687,7 +697,8 @@ static int map_lookup_elem(union bpf_attr *attr)  	if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||  	    map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || -	    map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) +	    map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY || +	    map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)  		value_size = round_up(map->value_size, 8) * num_possible_cpus();  	else if (IS_FD_MAP(map))  		value_size = sizeof(u32); @@ -706,6 +717,8 @@ static int map_lookup_elem(union bpf_attr *attr)  		err = bpf_percpu_hash_copy(map, key, value);  	} else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {  		err = bpf_percpu_array_copy(map, key, value); +	} else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { +		err = bpf_percpu_cgroup_storage_copy(map, key, value);  	} else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) {  		err = bpf_stackmap_copy(map, key, value);  	} else if (IS_FD_ARRAY(map)) { @@ -714,13 +727,21 @@ static int map_lookup_elem(union bpf_attr *attr)  		err = bpf_fd_htab_map_lookup_elem(map, key, value);  	} else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) {  		err = bpf_fd_reuseport_array_lookup_elem(map, key, value); +	} else if (map->map_type == BPF_MAP_TYPE_QUEUE || +		   map->map_type == BPF_MAP_TYPE_STACK) { +		err = map->ops->map_peek_elem(map, value);  	} else {  		rcu_read_lock();  		ptr = map->ops->map_lookup_elem(map, key); -		if (ptr) +		if (IS_ERR(ptr)) { +			err = PTR_ERR(ptr); +		} else if (!ptr) { +			err = -ENOENT; +		} else { +			err = 0;  			memcpy(value, ptr, value_size); +		}  		rcu_read_unlock(); -		err = ptr ? 0 : -ENOENT;  	}  	if (err) @@ -741,6 +762,17 @@ err_put:  	return err;  } +static void maybe_wait_bpf_programs(struct bpf_map *map) +{ +	/* Wait for any running BPF programs to complete so that +	 * userspace, when we return to it, knows that all programs +	 * that could be running use the new map value. +	 */ +	if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS || +	    map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) +		synchronize_rcu(); +} +  #define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags  static int map_update_elem(union bpf_attr *attr) @@ -767,7 +799,7 @@ static int map_update_elem(union bpf_attr *attr)  		goto err_put;  	} -	key = memdup_user(ukey, map->key_size); +	key = __bpf_copy_key(ukey, map->key_size);  	if (IS_ERR(key)) {  		err = PTR_ERR(key);  		goto err_put; @@ -775,7 +807,8 @@ static int map_update_elem(union bpf_attr *attr)  	if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH ||  	    map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || -	    map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) +	    map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY || +	    map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE)  		value_size = round_up(map->value_size, 8) * num_possible_cpus();  	else  		value_size = map->value_size; @@ -810,6 +843,9 @@ static int map_update_elem(union bpf_attr *attr)  		err = bpf_percpu_hash_update(map, key, value, attr->flags);  	} else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {  		err = bpf_percpu_array_update(map, key, value, attr->flags); +	} else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { +		err = bpf_percpu_cgroup_storage_update(map, key, value, +						       attr->flags);  	} else if (IS_FD_ARRAY(map)) {  		rcu_read_lock();  		err = bpf_fd_array_map_update_elem(map, f.file, key, value, @@ -824,6 +860,9 @@ static int map_update_elem(union bpf_attr *attr)  		/* rcu_read_lock() is not needed */  		err = bpf_fd_reuseport_array_update_elem(map, key, value,  							 attr->flags); +	} else if (map->map_type == BPF_MAP_TYPE_QUEUE || +		   map->map_type == BPF_MAP_TYPE_STACK) { +		err = map->ops->map_push_elem(map, value, attr->flags);  	} else {  		rcu_read_lock();  		err = map->ops->map_update_elem(map, key, value, attr->flags); @@ -831,6 +870,7 @@ static int map_update_elem(union bpf_attr *attr)  	}  	__this_cpu_dec(bpf_prog_active);  	preempt_enable(); +	maybe_wait_bpf_programs(map);  out:  free_value:  	kfree(value); @@ -865,7 +905,7 @@ static int map_delete_elem(union bpf_attr *attr)  		goto err_put;  	} -	key = memdup_user(ukey, map->key_size); +	key = __bpf_copy_key(ukey, map->key_size);  	if (IS_ERR(key)) {  		err = PTR_ERR(key);  		goto err_put; @@ -883,6 +923,7 @@ static int map_delete_elem(union bpf_attr *attr)  	rcu_read_unlock();  	__this_cpu_dec(bpf_prog_active);  	preempt_enable(); +	maybe_wait_bpf_programs(map);  out:  	kfree(key);  err_put: @@ -917,7 +958,7 @@ static int map_get_next_key(union bpf_attr *attr)  	}  	if (ukey) { -		key = memdup_user(ukey, map->key_size); +		key = __bpf_copy_key(ukey, map->key_size);  		if (IS_ERR(key)) {  			err = PTR_ERR(key);  			goto err_put; @@ -958,6 +999,69 @@ err_put:  	return err;  } +#define BPF_MAP_LOOKUP_AND_DELETE_ELEM_LAST_FIELD value + +static int map_lookup_and_delete_elem(union bpf_attr *attr) +{ +	void __user *ukey = u64_to_user_ptr(attr->key); +	void __user *uvalue = u64_to_user_ptr(attr->value); +	int ufd = attr->map_fd; +	struct bpf_map *map; +	void *key, *value; +	u32 value_size; +	struct fd f; +	int err; + +	if (CHECK_ATTR(BPF_MAP_LOOKUP_AND_DELETE_ELEM)) +		return -EINVAL; + +	f = fdget(ufd); +	map = __bpf_map_get(f); +	if (IS_ERR(map)) +		return PTR_ERR(map); + +	if (!(f.file->f_mode & FMODE_CAN_WRITE)) { +		err = -EPERM; +		goto err_put; +	} + +	key = __bpf_copy_key(ukey, map->key_size); +	if (IS_ERR(key)) { +		err = PTR_ERR(key); +		goto err_put; +	} + +	value_size = map->value_size; + +	err = -ENOMEM; +	value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); +	if (!value) +		goto free_key; + +	if (map->map_type == BPF_MAP_TYPE_QUEUE || +	    map->map_type == BPF_MAP_TYPE_STACK) { +		err = map->ops->map_pop_elem(map, value); +	} else { +		err = -ENOTSUPP; +	} + +	if (err) +		goto free_value; + +	if (copy_to_user(uvalue, value, value_size) != 0) +		goto free_value; + +	err = 0; + +free_value: +	kfree(value); +free_key: +	kfree(key); +err_put: +	fdput(f); +	return err; +} +  static const struct bpf_prog_ops * const bpf_prog_types[] = {  #define BPF_PROG_TYPE(_id, _name) \  	[_id] = & _name ## _prog_ops, @@ -989,10 +1093,15 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog)  /* drop refcnt on maps used by eBPF program and free auxilary data */  static void free_used_maps(struct bpf_prog_aux *aux)  { +	enum bpf_cgroup_storage_type stype;  	int i; -	if (aux->cgroup_storage) -		bpf_cgroup_storage_release(aux->prog, aux->cgroup_storage); +	for_each_cgroup_storage_type(stype) { +		if (!aux->cgroup_storage[stype]) +			continue; +		bpf_cgroup_storage_release(aux->prog, +					   aux->cgroup_storage[stype]); +	}  	for (i = 0; i < aux->used_map_cnt; i++)  		bpf_map_put(aux->used_maps[i]); @@ -1616,6 +1725,9 @@ static int bpf_prog_attach(const union bpf_attr *attr)  	case BPF_LIRC_MODE2:  		ptype = BPF_PROG_TYPE_LIRC_MODE2;  		break; +	case BPF_FLOW_DISSECTOR: +		ptype = BPF_PROG_TYPE_FLOW_DISSECTOR; +		break;  	default:  		return -EINVAL;  	} @@ -1632,11 +1744,14 @@ static int bpf_prog_attach(const union bpf_attr *attr)  	switch (ptype) {  	case BPF_PROG_TYPE_SK_SKB:  	case BPF_PROG_TYPE_SK_MSG: -		ret = sockmap_get_from_fd(attr, ptype, prog); +		ret = sock_map_get_from_fd(attr, prog);  		break;  	case BPF_PROG_TYPE_LIRC_MODE2:  		ret = lirc_prog_attach(attr, prog);  		break; +	case BPF_PROG_TYPE_FLOW_DISSECTOR: +		ret = skb_flow_dissector_bpf_prog_attach(attr, prog); +		break;  	default:  		ret = cgroup_bpf_prog_attach(attr, ptype, prog);  	} @@ -1683,12 +1798,14 @@ static int bpf_prog_detach(const union bpf_attr *attr)  		ptype = BPF_PROG_TYPE_CGROUP_DEVICE;  		break;  	case BPF_SK_MSG_VERDICT: -		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_MSG, NULL); +		return sock_map_get_from_fd(attr, NULL);  	case BPF_SK_SKB_STREAM_PARSER:  	case BPF_SK_SKB_STREAM_VERDICT: -		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, NULL); +		return sock_map_get_from_fd(attr, NULL);  	case BPF_LIRC_MODE2:  		return lirc_prog_detach(attr); +	case BPF_FLOW_DISSECTOR: +		return skb_flow_dissector_bpf_prog_detach(attr);  	default:  		return -EINVAL;  	} @@ -2418,6 +2535,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz  	case BPF_TASK_FD_QUERY:  		err = bpf_task_fd_query(&attr, uattr);  		break; +	case BPF_MAP_LOOKUP_AND_DELETE_ELEM: +		err = map_lookup_and_delete_elem(&attr); +		break;  	default:  		err = -EINVAL;  		break; |