Commit aaac3ba9 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by David S. Miller

bpf: charge user for creation of BPF maps and programs

since eBPF programs and maps use kernel memory consider it 'locked' memory
from user accounting point of view and charge it against RLIMIT_MEMLOCK limit.
This limit is typically set to 64Kbytes by distros, so almost all
bpf+tracing programs would need to increase it, since they use maps,
but kernel charges maximum map size upfront.
For example the hash map of 1024 elements will be charged as 64Kbyte.
It's inconvenient for current users and changes current behavior for root,
but probably worth doing to be consistent root vs non-root.

Similar accounting logic is done by mmap of perf_event.
Signed-off-by: default avatarAlexei Starovoitov <ast@plumgrid.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1be7f75d
...@@ -36,6 +36,8 @@ struct bpf_map { ...@@ -36,6 +36,8 @@ struct bpf_map {
u32 key_size; u32 key_size;
u32 value_size; u32 value_size;
u32 max_entries; u32 max_entries;
u32 pages;
struct user_struct *user;
const struct bpf_map_ops *ops; const struct bpf_map_ops *ops;
struct work_struct work; struct work_struct work;
}; };
...@@ -128,6 +130,7 @@ struct bpf_prog_aux { ...@@ -128,6 +130,7 @@ struct bpf_prog_aux {
const struct bpf_verifier_ops *ops; const struct bpf_verifier_ops *ops;
struct bpf_map **used_maps; struct bpf_map **used_maps;
struct bpf_prog *prog; struct bpf_prog *prog;
struct user_struct *user;
union { union {
struct work_struct work; struct work_struct work;
struct rcu_head rcu; struct rcu_head rcu;
......
...@@ -840,7 +840,7 @@ struct user_struct { ...@@ -840,7 +840,7 @@ struct user_struct {
struct hlist_node uidhash_node; struct hlist_node uidhash_node;
kuid_t uid; kuid_t uid;
#ifdef CONFIG_PERF_EVENTS #if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL)
atomic_long_t locked_vm; atomic_long_t locked_vm;
#endif #endif
}; };
......
...@@ -49,7 +49,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) ...@@ -49,7 +49,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
array->map.key_size = attr->key_size; array->map.key_size = attr->key_size;
array->map.value_size = attr->value_size; array->map.value_size = attr->value_size;
array->map.max_entries = attr->max_entries; array->map.max_entries = attr->max_entries;
array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
array->elem_size = elem_size; array->elem_size = elem_size;
return &array->map; return &array->map;
......
...@@ -88,6 +88,10 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) ...@@ -88,6 +88,10 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr)
htab->elem_size = sizeof(struct htab_elem) + htab->elem_size = sizeof(struct htab_elem) +
round_up(htab->map.key_size, 8) + round_up(htab->map.key_size, 8) +
htab->map.value_size; htab->map.value_size;
htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) +
htab->elem_size * htab->map.max_entries,
PAGE_SIZE) >> PAGE_SHIFT;
return &htab->map; return &htab->map;
free_htab: free_htab:
......
...@@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl) ...@@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl)
list_add(&tl->list_node, &bpf_map_types); list_add(&tl->list_node, &bpf_map_types);
} }
static int bpf_map_charge_memlock(struct bpf_map *map)
{
struct user_struct *user = get_current_user();
unsigned long memlock_limit;
memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
atomic_long_add(map->pages, &user->locked_vm);
if (atomic_long_read(&user->locked_vm) > memlock_limit) {
atomic_long_sub(map->pages, &user->locked_vm);
free_uid(user);
return -EPERM;
}
map->user = user;
return 0;
}
static void bpf_map_uncharge_memlock(struct bpf_map *map)
{
struct user_struct *user = map->user;
atomic_long_sub(map->pages, &user->locked_vm);
free_uid(user);
}
/* called from workqueue */ /* called from workqueue */
static void bpf_map_free_deferred(struct work_struct *work) static void bpf_map_free_deferred(struct work_struct *work)
{ {
struct bpf_map *map = container_of(work, struct bpf_map, work); struct bpf_map *map = container_of(work, struct bpf_map, work);
bpf_map_uncharge_memlock(map);
/* implementation dependent freeing */ /* implementation dependent freeing */
map->ops->map_free(map); map->ops->map_free(map);
} }
...@@ -110,6 +137,10 @@ static int map_create(union bpf_attr *attr) ...@@ -110,6 +137,10 @@ static int map_create(union bpf_attr *attr)
atomic_set(&map->refcnt, 1); atomic_set(&map->refcnt, 1);
err = bpf_map_charge_memlock(map);
if (err)
goto free_map;
err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC); err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC);
if (err < 0) if (err < 0)
...@@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux) ...@@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux)
kfree(aux->used_maps); kfree(aux->used_maps);
} }
static int bpf_prog_charge_memlock(struct bpf_prog *prog)
{
struct user_struct *user = get_current_user();
unsigned long memlock_limit;
memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
atomic_long_add(prog->pages, &user->locked_vm);
if (atomic_long_read(&user->locked_vm) > memlock_limit) {
atomic_long_sub(prog->pages, &user->locked_vm);
free_uid(user);
return -EPERM;
}
prog->aux->user = user;
return 0;
}
static void bpf_prog_uncharge_memlock(struct bpf_prog *prog)
{
struct user_struct *user = prog->aux->user;
atomic_long_sub(prog->pages, &user->locked_vm);
free_uid(user);
}
static void __prog_put_rcu(struct rcu_head *rcu) static void __prog_put_rcu(struct rcu_head *rcu)
{ {
struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
free_used_maps(aux); free_used_maps(aux);
bpf_prog_uncharge_memlock(aux->prog);
bpf_prog_free(aux->prog); bpf_prog_free(aux->prog);
} }
...@@ -554,6 +611,10 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -554,6 +611,10 @@ static int bpf_prog_load(union bpf_attr *attr)
if (!prog) if (!prog)
return -ENOMEM; return -ENOMEM;
err = bpf_prog_charge_memlock(prog);
if (err)
goto free_prog_nouncharge;
prog->len = attr->insn_cnt; prog->len = attr->insn_cnt;
err = -EFAULT; err = -EFAULT;
...@@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr) ...@@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr)
free_used_maps: free_used_maps:
free_used_maps(prog->aux); free_used_maps(prog->aux);
free_prog: free_prog:
bpf_prog_uncharge_memlock(prog);
free_prog_nouncharge:
bpf_prog_free(prog); bpf_prog_free(prog);
return err; return err;
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment