|
@@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl)
|
|
|
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 */
|
|
|
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);
|
|
|
/* implementation dependent freeing */
|
|
|
map->ops->map_free(map);
|
|
|
}
|
|
@@ -110,6 +137,10 @@ static int map_create(union bpf_attr *attr)
|
|
|
|
|
|
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);
|
|
|
|
|
|
if (err < 0)
|
|
@@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux)
|
|
|
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)
|
|
|
{
|
|
|
struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
|
|
|
|
|
|
free_used_maps(aux);
|
|
|
+ bpf_prog_uncharge_memlock(aux->prog);
|
|
|
bpf_prog_free(aux->prog);
|
|
|
}
|
|
|
|
|
@@ -554,6 +611,10 @@ static int bpf_prog_load(union bpf_attr *attr)
|
|
|
if (!prog)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ err = bpf_prog_charge_memlock(prog);
|
|
|
+ if (err)
|
|
|
+ goto free_prog_nouncharge;
|
|
|
+
|
|
|
prog->len = attr->insn_cnt;
|
|
|
|
|
|
err = -EFAULT;
|
|
@@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr)
|
|
|
free_used_maps:
|
|
|
free_used_maps(prog->aux);
|
|
|
free_prog:
|
|
|
+ bpf_prog_uncharge_memlock(prog);
|
|
|
+free_prog_nouncharge:
|
|
|
bpf_prog_free(prog);
|
|
|
return err;
|
|
|
}
|