diff options
Diffstat (limited to 'kernel/bpf/syscall.c')
| -rw-r--r-- | kernel/bpf/syscall.c | 222 | 
1 files changed, 113 insertions, 109 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0fa20624707f..8339d81cba1d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -103,6 +103,7 @@ int bpf_check_uarg_tail_zero(void __user *uaddr,  const struct bpf_map_ops bpf_map_offload_ops = {  	.map_alloc = bpf_map_offload_map_alloc,  	.map_free = bpf_map_offload_map_free, +	.map_check_btf = map_check_no_btf,  };  static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) @@ -181,32 +182,60 @@ int bpf_map_precharge_memlock(u32 pages)  	return 0;  } -static int bpf_map_charge_memlock(struct bpf_map *map) +static int bpf_charge_memlock(struct user_struct *user, u32 pages)  { -	struct user_struct *user = get_current_user(); -	unsigned long memlock_limit; +	unsigned long memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; -	memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; +	if (atomic_long_add_return(pages, &user->locked_vm) > memlock_limit) { +		atomic_long_sub(pages, &user->locked_vm); +		return -EPERM; +	} +	return 0; +} + +static void bpf_uncharge_memlock(struct user_struct *user, u32 pages) +{ +	atomic_long_sub(pages, &user->locked_vm); +} -	atomic_long_add(map->pages, &user->locked_vm); +static int bpf_map_init_memlock(struct bpf_map *map) +{ +	struct user_struct *user = get_current_user(); +	int ret; -	if (atomic_long_read(&user->locked_vm) > memlock_limit) { -		atomic_long_sub(map->pages, &user->locked_vm); +	ret = bpf_charge_memlock(user, map->pages); +	if (ret) {  		free_uid(user); -		return -EPERM; +		return ret;  	}  	map->user = user; -	return 0; +	return ret;  } -static void bpf_map_uncharge_memlock(struct bpf_map *map) +static void bpf_map_release_memlock(struct bpf_map *map)  {  	struct user_struct *user = map->user; - -	atomic_long_sub(map->pages, &user->locked_vm); +	bpf_uncharge_memlock(user, map->pages);  	free_uid(user);  } +int bpf_map_charge_memlock(struct bpf_map *map, u32 pages) +{ +	int ret; + +	ret = bpf_charge_memlock(map->user, pages); +	if (ret) +		return ret; +	map->pages += pages; +	return ret; +} + +void bpf_map_uncharge_memlock(struct bpf_map *map, u32 pages) +{ +	bpf_uncharge_memlock(map->user, pages); +	map->pages -= pages; +} +  static int bpf_map_alloc_id(struct bpf_map *map)  {  	int id; @@ -256,7 +285,7 @@ static void bpf_map_free_deferred(struct work_struct *work)  {  	struct bpf_map *map = container_of(work, struct bpf_map, work); -	bpf_map_uncharge_memlock(map); +	bpf_map_release_memlock(map);  	security_bpf_map_free(map);  	/* implementation dependent freeing */  	map->ops->map_free(map); @@ -427,6 +456,34 @@ static int bpf_obj_name_cpy(char *dst, const char *src)  	return 0;  } +int map_check_no_btf(const struct bpf_map *map, +		     const struct btf_type *key_type, +		     const struct btf_type *value_type) +{ +	return -ENOTSUPP; +} + +static int map_check_btf(const struct bpf_map *map, const struct btf *btf, +			 u32 btf_key_id, u32 btf_value_id) +{ +	const struct btf_type *key_type, *value_type; +	u32 key_size, value_size; +	int ret = 0; + +	key_type = btf_type_id_size(btf, &btf_key_id, &key_size); +	if (!key_type || key_size != map->key_size) +		return -EINVAL; + +	value_type = btf_type_id_size(btf, &btf_value_id, &value_size); +	if (!value_type || value_size != map->value_size) +		return -EINVAL; + +	if (map->ops->map_check_btf) +		ret = map->ops->map_check_btf(map, key_type, value_type); + +	return ret; +} +  #define BPF_MAP_CREATE_LAST_FIELD btf_value_type_id  /* called via syscall */  static int map_create(union bpf_attr *attr) @@ -461,8 +518,7 @@ static int map_create(union bpf_attr *attr)  	atomic_set(&map->refcnt, 1);  	atomic_set(&map->usercnt, 1); -	if (bpf_map_support_seq_show(map) && -	    (attr->btf_key_type_id || attr->btf_value_type_id)) { +	if (attr->btf_key_type_id || attr->btf_value_type_id) {  		struct btf *btf;  		if (!attr->btf_key_type_id || !attr->btf_value_type_id) { @@ -476,8 +532,8 @@ static int map_create(union bpf_attr *attr)  			goto free_map_nouncharge;  		} -		err = map->ops->map_check_btf(map, btf, attr->btf_key_type_id, -					      attr->btf_value_type_id); +		err = map_check_btf(map, btf, attr->btf_key_type_id, +				    attr->btf_value_type_id);  		if (err) {  			btf_put(btf);  			goto free_map_nouncharge; @@ -492,7 +548,7 @@ static int map_create(union bpf_attr *attr)  	if (err)  		goto free_map_nouncharge; -	err = bpf_map_charge_memlock(map); +	err = bpf_map_init_memlock(map);  	if (err)  		goto free_map_sec; @@ -515,7 +571,7 @@ static int map_create(union bpf_attr *attr)  	return err;  free_map: -	bpf_map_uncharge_memlock(map); +	bpf_map_release_memlock(map);  free_map_sec:  	security_bpf_map_free(map);  free_map_nouncharge: @@ -575,7 +631,7 @@ static struct bpf_map *bpf_map_inc_not_zero(struct bpf_map *map,  {  	int refold; -	refold = __atomic_add_unless(&map->refcnt, 1, 0); +	refold = atomic_fetch_add_unless(&map->refcnt, 1, 0);  	if (refold >= BPF_MAX_REFCNT) {  		__bpf_map_put(map, false); @@ -656,6 +712,8 @@ static int map_lookup_elem(union bpf_attr *attr)  		err = bpf_fd_array_map_lookup_elem(map, key, value);  	} else if (IS_FD_HASH(map)) {  		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 {  		rcu_read_lock();  		ptr = map->ops->map_lookup_elem(map, key); @@ -735,7 +793,9 @@ static int map_update_elem(union bpf_attr *attr)  	if (bpf_map_is_dev_bound(map)) {  		err = bpf_map_offload_update_elem(map, key, value, attr->flags);  		goto out; -	} else if (map->map_type == BPF_MAP_TYPE_CPUMAP) { +	} else if (map->map_type == BPF_MAP_TYPE_CPUMAP || +		   map->map_type == BPF_MAP_TYPE_SOCKHASH || +		   map->map_type == BPF_MAP_TYPE_SOCKMAP) {  		err = map->ops->map_update_elem(map, key, value, attr->flags);  		goto out;  	} @@ -760,6 +820,10 @@ static int map_update_elem(union bpf_attr *attr)  		err = bpf_fd_htab_map_update_elem(map, f.file, key, value,  						  attr->flags);  		rcu_read_unlock(); +	} else if (map->map_type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) { +		/* rcu_read_lock() is not needed */ +		err = bpf_fd_reuseport_array_update_elem(map, key, value, +							 attr->flags);  	} else {  		rcu_read_lock();  		err = map->ops->map_update_elem(map, key, value, attr->flags); @@ -927,6 +991,9 @@ static void free_used_maps(struct bpf_prog_aux *aux)  {  	int i; +	if (aux->cgroup_storage) +		bpf_cgroup_storage_release(aux->prog, aux->cgroup_storage); +  	for (i = 0; i < aux->used_map_cnt; i++)  		bpf_map_put(aux->used_maps[i]); @@ -1034,14 +1101,9 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)  static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)  {  	if (atomic_dec_and_test(&prog->aux->refcnt)) { -		int i; -  		/* bpf_prog_free_id() must be called first */  		bpf_prog_free_id(prog, do_idr_lock); - -		for (i = 0; i < prog->aux->func_cnt; i++) -			bpf_prog_kallsyms_del(prog->aux->func[i]); -		bpf_prog_kallsyms_del(prog); +		bpf_prog_kallsyms_del_all(prog);  		call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);  	} @@ -1147,7 +1209,7 @@ struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog)  {  	int refold; -	refold = __atomic_add_unless(&prog->aux->refcnt, 1, 0); +	refold = atomic_fetch_add_unless(&prog->aux->refcnt, 1, 0);  	if (refold >= BPF_MAX_REFCNT) {  		__bpf_prog_put(prog, false); @@ -1358,9 +1420,7 @@ static int bpf_prog_load(union bpf_attr *attr)  	if (err < 0)  		goto free_used_maps; -	/* eBPF program is ready to be JITed */ -	if (!prog->bpf_func) -		prog = bpf_prog_select_runtime(prog, &err); +	prog = bpf_prog_select_runtime(prog, &err);  	if (err < 0)  		goto free_used_maps; @@ -1384,6 +1444,7 @@ static int bpf_prog_load(union bpf_attr *attr)  	return err;  free_used_maps: +	bpf_prog_kallsyms_del_subprogs(prog);  	free_used_maps(prog->aux);  free_prog:  	bpf_prog_uncharge_memlock(prog); @@ -1489,8 +1550,6 @@ out_free_tp:  	return err;  } -#ifdef CONFIG_CGROUP_BPF -  static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,  					     enum bpf_attach_type attach_type)  { @@ -1505,40 +1564,6 @@ static int bpf_prog_attach_check_attach_type(const struct bpf_prog *prog,  #define BPF_PROG_ATTACH_LAST_FIELD attach_flags -static int sockmap_get_from_fd(const union bpf_attr *attr, -			       int type, bool attach) -{ -	struct bpf_prog *prog = NULL; -	int ufd = attr->target_fd; -	struct bpf_map *map; -	struct fd f; -	int err; - -	f = fdget(ufd); -	map = __bpf_map_get(f); -	if (IS_ERR(map)) -		return PTR_ERR(map); - -	if (attach) { -		prog = bpf_prog_get_type(attr->attach_bpf_fd, type); -		if (IS_ERR(prog)) { -			fdput(f); -			return PTR_ERR(prog); -		} -	} - -	err = sock_map_prog(map, prog, attr->attach_type); -	if (err) { -		fdput(f); -		if (prog) -			bpf_prog_put(prog); -		return err; -	} - -	fdput(f); -	return 0; -} -  #define BPF_F_ATTACH_MASK \  	(BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI) @@ -1546,7 +1571,6 @@ static int bpf_prog_attach(const union bpf_attr *attr)  {  	enum bpf_prog_type ptype;  	struct bpf_prog *prog; -	struct cgroup *cgrp;  	int ret;  	if (!capable(CAP_NET_ADMIN)) @@ -1583,12 +1607,15 @@ static int bpf_prog_attach(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, true); +		ptype = BPF_PROG_TYPE_SK_MSG; +		break;  	case BPF_SK_SKB_STREAM_PARSER:  	case BPF_SK_SKB_STREAM_VERDICT: -		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true); +		ptype = BPF_PROG_TYPE_SK_SKB; +		break;  	case BPF_LIRC_MODE2: -		return lirc_prog_attach(attr); +		ptype = BPF_PROG_TYPE_LIRC_MODE2; +		break;  	default:  		return -EINVAL;  	} @@ -1602,18 +1629,20 @@ static int bpf_prog_attach(const union bpf_attr *attr)  		return -EINVAL;  	} -	cgrp = cgroup_get_from_fd(attr->target_fd); -	if (IS_ERR(cgrp)) { -		bpf_prog_put(prog); -		return PTR_ERR(cgrp); +	switch (ptype) { +	case BPF_PROG_TYPE_SK_SKB: +	case BPF_PROG_TYPE_SK_MSG: +		ret = sockmap_get_from_fd(attr, ptype, prog); +		break; +	case BPF_PROG_TYPE_LIRC_MODE2: +		ret = lirc_prog_attach(attr, prog); +		break; +	default: +		ret = cgroup_bpf_prog_attach(attr, ptype, prog);  	} -	ret = cgroup_bpf_attach(cgrp, prog, attr->attach_type, -				attr->attach_flags);  	if (ret)  		bpf_prog_put(prog); -	cgroup_put(cgrp); -  	return ret;  } @@ -1622,9 +1651,6 @@ static int bpf_prog_attach(const union bpf_attr *attr)  static int bpf_prog_detach(const union bpf_attr *attr)  {  	enum bpf_prog_type ptype; -	struct bpf_prog *prog; -	struct cgroup *cgrp; -	int ret;  	if (!capable(CAP_NET_ADMIN))  		return -EPERM; @@ -1657,29 +1683,17 @@ 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, false); +		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_MSG, NULL);  	case BPF_SK_SKB_STREAM_PARSER:  	case BPF_SK_SKB_STREAM_VERDICT: -		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false); +		return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, NULL);  	case BPF_LIRC_MODE2:  		return lirc_prog_detach(attr);  	default:  		return -EINVAL;  	} -	cgrp = cgroup_get_from_fd(attr->target_fd); -	if (IS_ERR(cgrp)) -		return PTR_ERR(cgrp); - -	prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); -	if (IS_ERR(prog)) -		prog = NULL; - -	ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type, 0); -	if (prog) -		bpf_prog_put(prog); -	cgroup_put(cgrp); -	return ret; +	return cgroup_bpf_prog_detach(attr, ptype);  }  #define BPF_PROG_QUERY_LAST_FIELD query.prog_cnt @@ -1687,9 +1701,6 @@ static int bpf_prog_detach(const union bpf_attr *attr)  static int bpf_prog_query(const union bpf_attr *attr,  			  union bpf_attr __user *uattr)  { -	struct cgroup *cgrp; -	int ret; -  	if (!capable(CAP_NET_ADMIN))  		return -EPERM;  	if (CHECK_ATTR(BPF_PROG_QUERY)) @@ -1717,14 +1728,9 @@ static int bpf_prog_query(const union bpf_attr *attr,  	default:  		return -EINVAL;  	} -	cgrp = cgroup_get_from_fd(attr->query.target_fd); -	if (IS_ERR(cgrp)) -		return PTR_ERR(cgrp); -	ret = cgroup_bpf_query(cgrp, attr, uattr); -	cgroup_put(cgrp); -	return ret; + +	return cgroup_bpf_prog_query(attr, uattr);  } -#endif /* CONFIG_CGROUP_BPF */  #define BPF_PROG_TEST_RUN_LAST_FIELD test.duration @@ -2371,7 +2377,6 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz  	case BPF_OBJ_GET:  		err = bpf_obj_get(&attr);  		break; -#ifdef CONFIG_CGROUP_BPF  	case BPF_PROG_ATTACH:  		err = bpf_prog_attach(&attr);  		break; @@ -2381,7 +2386,6 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz  	case BPF_PROG_QUERY:  		err = bpf_prog_query(&attr, uattr);  		break; -#endif  	case BPF_PROG_TEST_RUN:  		err = bpf_prog_test_run(&attr, uattr);  		break;  |