History

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.

Alexei Starovoitov committed 11 years ago

Commit aaac3ba1 parent 1be7f755 files · +72 −2Browse files

Unified · Split

5 files
  1. include/linux/bpf.h +3 −0
  2. include/linux/sched.h +1 −1
  3. kernel/bpf/arraymap.c +1 −1
  4. kernel/bpf/hashtab.c +4 −0
  5. kernel/bpf/syscall.c +63 −0
include/linux/bpf.h+3−0
@@ -36,6 +36,8 @@ struct bpf_map { · 35 unmodified lines
36 u32 key_size;36 u32 key_size;
37 u32 value_size;37 u32 value_size;
38 u32 max_entries;38 u32 max_entries;
39 u32 pages;
40 struct user_struct *user;
39 const struct bpf_map_ops *ops;41 const struct bpf_map_ops *ops;
40 struct work_struct work;42 struct work_struct work;
41};43};
@@ -128,6 +130,7 @@ struct bpf_prog_aux { · 86 unmodified lines
128 const struct bpf_verifier_ops *ops;130 const struct bpf_verifier_ops *ops;
129 struct bpf_map **used_maps;131 struct bpf_map **used_maps;
130 struct bpf_prog *prog;132 struct bpf_prog *prog;
133 struct user_struct *user;
131 union {134 union {
132 struct work_struct work;135 struct work_struct work;
133 struct rcu_head rcu;136 struct rcu_head rcu;
include/linux/sched.h+1−1
@@ -840,7 +840,7 @@ struct user_struct { · 839 unmodified lines
840 struct hlist_node uidhash_node;840 struct hlist_node uidhash_node;
841 kuid_t uid;841 kuid_t uid;
842 842
843#ifdef CONFIG_PERF_EVENTS843#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL)
844 atomic_long_t locked_vm;844 atomic_long_t locked_vm;
845#endif845#endif
846};846};
kernel/bpf/arraymap.c+1−1
@@ -49,7 +49,7 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) · 48 unmodified lines
49 array->map.key_size = attr->key_size;49 array->map.key_size = attr->key_size;
50 array->map.value_size = attr->value_size;50 array->map.value_size = attr->value_size;
51 array->map.max_entries = attr->max_entries;51 array->map.max_entries = attr->max_entries;
52 52 array->map.pages = round_up(array_size, PAGE_SIZE) >> PAGE_SHIFT;
53 array->elem_size = elem_size;53 array->elem_size = elem_size;
54 54
55 return &array->map;55 return &array->map;
kernel/bpf/hashtab.c+4−0
@@ -88,6 +88,10 @@ static struct bpf_map *htab_map_alloc(union bpf_attr *attr) · 87 unmodified lines
88 htab->elem_size = sizeof(struct htab_elem) +88 htab->elem_size = sizeof(struct htab_elem) +
89 round_up(htab->map.key_size, 8) +89 round_up(htab->map.key_size, 8) +
90 htab->map.value_size;90 htab->map.value_size;
91
92 htab->map.pages = round_up(htab->n_buckets * sizeof(struct hlist_head) +
93 htab->elem_size * htab->map.max_entries,
94 PAGE_SIZE) >> PAGE_SHIFT;
91 return &htab->map;95 return &htab->map;
92 96
93free_htab:97free_htab:
kernel/bpf/syscall.c+63−0
@@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl) · 45 unmodified lines
46 list_add(&tl->list_node, &bpf_map_types);46 list_add(&tl->list_node, &bpf_map_types);
47}47}
48 48
49static int bpf_map_charge_memlock(struct bpf_map *map)
50{
51 struct user_struct *user = get_current_user();
52 unsigned long memlock_limit;
53
54 memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
55
56 atomic_long_add(map->pages, &user->locked_vm);
57
58 if (atomic_long_read(&user->locked_vm) > memlock_limit) {
59 atomic_long_sub(map->pages, &user->locked_vm);
60 free_uid(user);
61 return -EPERM;
62 }
63 map->user = user;
64 return 0;
65}
66
67static void bpf_map_uncharge_memlock(struct bpf_map *map)
68{
69 struct user_struct *user = map->user;
70
71 atomic_long_sub(map->pages, &user->locked_vm);
72 free_uid(user);
73}
74
49/* called from workqueue */75/* called from workqueue */
50static void bpf_map_free_deferred(struct work_struct *work)76static void bpf_map_free_deferred(struct work_struct *work)
51{77{
52 struct bpf_map *map = container_of(work, struct bpf_map, work);78 struct bpf_map *map = container_of(work, struct bpf_map, work);
53 79
80 bpf_map_uncharge_memlock(map);
54 /* implementation dependent freeing */81 /* implementation dependent freeing */
55 map->ops->map_free(map);82 map->ops->map_free(map);
56}83}
@@ -110,6 +137,10 @@ static int map_create(union bpf_attr *attr) · 53 unmodified lines
110 137
111 atomic_set(&map->refcnt, 1);138 atomic_set(&map->refcnt, 1);
112 139
140 err = bpf_map_charge_memlock(map);
141 if (err)
142 goto free_map;
143
113 err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC);144 err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC);
114 145
115 if (err < 0)146 if (err < 0)
@@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux) · 326 unmodified lines
442 kfree(aux->used_maps);473 kfree(aux->used_maps);
443}474}
444 475
476static int bpf_prog_charge_memlock(struct bpf_prog *prog)
477{
478 struct user_struct *user = get_current_user();
479 unsigned long memlock_limit;
480
481 memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
482
483 atomic_long_add(prog->pages, &user->locked_vm);
484 if (atomic_long_read(&user->locked_vm) > memlock_limit) {
485 atomic_long_sub(prog->pages, &user->locked_vm);
486 free_uid(user);
487 return -EPERM;
488 }
489 prog->aux->user = user;
490 return 0;
491}
492
493static void bpf_prog_uncharge_memlock(struct bpf_prog *prog)
494{
495 struct user_struct *user = prog->aux->user;
496
497 atomic_long_sub(prog->pages, &user->locked_vm);
498 free_uid(user);
499}
500
445static void __prog_put_rcu(struct rcu_head *rcu)501static void __prog_put_rcu(struct rcu_head *rcu)
446{502{
447 struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);503 struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu);
448 504
449 free_used_maps(aux);505 free_used_maps(aux);
506 bpf_prog_uncharge_memlock(aux->prog);
450 bpf_prog_free(aux->prog);507 bpf_prog_free(aux->prog);
451}508}
452 509
@@ -554,6 +611,10 @@ static int bpf_prog_load(union bpf_attr *attr) · 101 unmodified lines
554 if (!prog)611 if (!prog)
555 return -ENOMEM;612 return -ENOMEM;
556 613
614 err = bpf_prog_charge_memlock(prog);
615 if (err)
616 goto free_prog_nouncharge;
617
557 prog->len = attr->insn_cnt;618 prog->len = attr->insn_cnt;
558 619
559 err = -EFAULT;620 err = -EFAULT;
@@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr) · 35 unmodified lines
595free_used_maps:656free_used_maps:
596 free_used_maps(prog->aux);657 free_used_maps(prog->aux);
597free_prog:658free_prog:
659 bpf_prog_uncharge_memlock(prog);
660free_prog_nouncharge:
598 bpf_prog_free(prog);661 bpf_prog_free(prog);
599 return err;662 return err;
600}663}