diff options
Diffstat (limited to 'kernel/bpf/syscall.c')
| -rw-r--r-- | kernel/bpf/syscall.c | 186 | 
1 files changed, 77 insertions, 109 deletions
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 821f9e807de5..13642c73dca0 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -27,30 +27,29 @@ DEFINE_PER_CPU(int, bpf_prog_active);  int sysctl_unprivileged_bpf_disabled __read_mostly; -static LIST_HEAD(bpf_map_types); +static const struct bpf_map_ops * const bpf_map_types[] = { +#define BPF_PROG_TYPE(_id, _ops) +#define BPF_MAP_TYPE(_id, _ops) \ +	[_id] = &_ops, +#include <linux/bpf_types.h> +#undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE +};  static struct bpf_map *find_and_alloc_map(union bpf_attr *attr)  { -	struct bpf_map_type_list *tl;  	struct bpf_map *map; -	list_for_each_entry(tl, &bpf_map_types, list_node) { -		if (tl->type == attr->map_type) { -			map = tl->ops->map_alloc(attr); -			if (IS_ERR(map)) -				return map; -			map->ops = tl->ops; -			map->map_type = attr->map_type; -			return map; -		} -	} -	return ERR_PTR(-EINVAL); -} +	if (attr->map_type >= ARRAY_SIZE(bpf_map_types) || +	    !bpf_map_types[attr->map_type]) +		return ERR_PTR(-EINVAL); -/* boot time registration of different map implementations */ -void bpf_register_map_type(struct bpf_map_type_list *tl) -{ -	list_add(&tl->list_node, &bpf_map_types); +	map = bpf_map_types[attr->map_type]->map_alloc(attr); +	if (IS_ERR(map)) +		return map; +	map->ops = bpf_map_types[attr->map_type]; +	map->map_type = attr->map_type; +	return map;  }  void *bpf_map_area_alloc(size_t size) @@ -215,7 +214,7 @@ int bpf_map_new_fd(struct bpf_map *map)  		   offsetof(union bpf_attr, CMD##_LAST_FIELD) - \  		   sizeof(attr->CMD##_LAST_FIELD)) != NULL -#define BPF_MAP_CREATE_LAST_FIELD map_flags +#define BPF_MAP_CREATE_LAST_FIELD inner_map_fd  /* called via syscall */  static int map_create(union bpf_attr *attr)  { @@ -352,6 +351,9 @@ static int map_lookup_elem(union bpf_attr *attr)  		err = bpf_percpu_array_copy(map, key, value);  	} else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) {  		err = bpf_stackmap_copy(map, key, value); +	} else if (map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS || +		   map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { +		err = -ENOTSUPP;  	} else {  		rcu_read_lock();  		ptr = map->ops->map_lookup_elem(map, key); @@ -438,11 +440,17 @@ static int map_update_elem(union bpf_attr *attr)  		err = bpf_percpu_array_update(map, key, value, attr->flags);  	} else if (map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY ||  		   map->map_type == BPF_MAP_TYPE_PROG_ARRAY || -		   map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY) { +		   map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || +		   map->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) {  		rcu_read_lock();  		err = bpf_fd_array_map_update_elem(map, f.file, key, value,  						   attr->flags);  		rcu_read_unlock(); +	} else if (map->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { +		rcu_read_lock(); +		err = bpf_fd_htab_map_update_elem(map, f.file, key, value, +						  attr->flags); +		rcu_read_unlock();  	} else {  		rcu_read_lock();  		err = map->ops->map_update_elem(map, key, value, attr->flags); @@ -528,14 +536,18 @@ static int map_get_next_key(union bpf_attr *attr)  	if (IS_ERR(map))  		return PTR_ERR(map); -	err = -ENOMEM; -	key = kmalloc(map->key_size, GFP_USER); -	if (!key) -		goto err_put; +	if (ukey) { +		err = -ENOMEM; +		key = kmalloc(map->key_size, GFP_USER); +		if (!key) +			goto err_put; -	err = -EFAULT; -	if (copy_from_user(key, ukey, map->key_size) != 0) -		goto free_key; +		err = -EFAULT; +		if (copy_from_user(key, ukey, map->key_size) != 0) +			goto free_key; +	} else { +		key = NULL; +	}  	err = -ENOMEM;  	next_key = kmalloc(map->key_size, GFP_USER); @@ -564,87 +576,23 @@ err_put:  	return err;  } -static LIST_HEAD(bpf_prog_types); +static const struct bpf_verifier_ops * const bpf_prog_types[] = { +#define BPF_PROG_TYPE(_id, _ops) \ +	[_id] = &_ops, +#define BPF_MAP_TYPE(_id, _ops) +#include <linux/bpf_types.h> +#undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE +};  static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog)  { -	struct bpf_prog_type_list *tl; - -	list_for_each_entry(tl, &bpf_prog_types, list_node) { -		if (tl->type == type) { -			prog->aux->ops = tl->ops; -			prog->type = type; -			return 0; -		} -	} - -	return -EINVAL; -} - -void bpf_register_prog_type(struct bpf_prog_type_list *tl) -{ -	list_add(&tl->list_node, &bpf_prog_types); -} - -/* fixup insn->imm field of bpf_call instructions: - * if (insn->imm == BPF_FUNC_map_lookup_elem) - *      insn->imm = bpf_map_lookup_elem - __bpf_call_base; - * else if (insn->imm == BPF_FUNC_map_update_elem) - *      insn->imm = bpf_map_update_elem - __bpf_call_base; - * else ... - * - * this function is called after eBPF program passed verification - */ -static void fixup_bpf_calls(struct bpf_prog *prog) -{ -	const struct bpf_func_proto *fn; -	int i; +	if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) +		return -EINVAL; -	for (i = 0; i < prog->len; i++) { -		struct bpf_insn *insn = &prog->insnsi[i]; - -		if (insn->code == (BPF_JMP | BPF_CALL)) { -			/* we reach here when program has bpf_call instructions -			 * and it passed bpf_check(), means that -			 * ops->get_func_proto must have been supplied, check it -			 */ -			BUG_ON(!prog->aux->ops->get_func_proto); - -			if (insn->imm == BPF_FUNC_get_route_realm) -				prog->dst_needed = 1; -			if (insn->imm == BPF_FUNC_get_prandom_u32) -				bpf_user_rnd_init_once(); -			if (insn->imm == BPF_FUNC_xdp_adjust_head) -				prog->xdp_adjust_head = 1; -			if (insn->imm == BPF_FUNC_tail_call) { -				/* If we tail call into other programs, we -				 * cannot make any assumptions since they -				 * can be replaced dynamically during runtime -				 * in the program array. -				 */ -				prog->cb_access = 1; -				prog->xdp_adjust_head = 1; - -				/* mark bpf_tail_call as different opcode -				 * to avoid conditional branch in -				 * interpeter for every normal call -				 * and to prevent accidental JITing by -				 * JIT compiler that doesn't support -				 * bpf_tail_call yet -				 */ -				insn->imm = 0; -				insn->code |= BPF_X; -				continue; -			} - -			fn = prog->aux->ops->get_func_proto(insn->imm); -			/* all functions that have prototype and verifier allowed -			 * programs to call them, must be real in-kernel functions -			 */ -			BUG_ON(!fn->func); -			insn->imm = fn->func - __bpf_call_base; -		} -	} +	prog->aux->ops = bpf_prog_types[type]; +	prog->type = type; +	return 0;  }  /* drop refcnt on maps used by eBPF program and free auxilary data */ @@ -900,9 +848,6 @@ static int bpf_prog_load(union bpf_attr *attr)  	if (err < 0)  		goto free_used_maps; -	/* fixup BPF_CALL->imm field */ -	fixup_bpf_calls(prog); -  	/* eBPF program is ready to be JITed */  	prog = bpf_prog_select_runtime(prog, &err);  	if (err < 0) @@ -1028,6 +973,28 @@ static int bpf_prog_detach(const union bpf_attr *attr)  }  #endif /* CONFIG_CGROUP_BPF */ +#define BPF_PROG_TEST_RUN_LAST_FIELD test.duration + +static int bpf_prog_test_run(const union bpf_attr *attr, +			     union bpf_attr __user *uattr) +{ +	struct bpf_prog *prog; +	int ret = -ENOTSUPP; + +	if (CHECK_ATTR(BPF_PROG_TEST_RUN)) +		return -EINVAL; + +	prog = bpf_prog_get(attr->test.prog_fd); +	if (IS_ERR(prog)) +		return PTR_ERR(prog); + +	if (prog->aux->ops->test_run) +		ret = prog->aux->ops->test_run(prog, attr, uattr); + +	bpf_prog_put(prog); +	return ret; +} +  SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)  {  	union bpf_attr attr = {}; @@ -1094,7 +1061,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); @@ -1103,7 +1069,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz  		err = bpf_prog_detach(&attr);  		break;  #endif - +	case BPF_PROG_TEST_RUN: +		err = bpf_prog_test_run(&attr, uattr); +		break;  	default:  		err = -EINVAL;  		break;  | 
