diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/bpf/helpers.c | 8 | ||||
-rw-r--r-- | kernel/bpf/local_storage.c | 150 | ||||
-rw-r--r-- | kernel/bpf/syscall.c | 11 | ||||
-rw-r--r-- | kernel/bpf/verifier.c | 15 |
4 files changed, 157 insertions, 27 deletions
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index e42f8789b7ea..6502115e8f55 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c | |||
@@ -206,10 +206,16 @@ BPF_CALL_2(bpf_get_local_storage, struct bpf_map *, map, u64, flags) | |||
206 | */ | 206 | */ |
207 | enum bpf_cgroup_storage_type stype = cgroup_storage_type(map); | 207 | enum bpf_cgroup_storage_type stype = cgroup_storage_type(map); |
208 | struct bpf_cgroup_storage *storage; | 208 | struct bpf_cgroup_storage *storage; |
209 | void *ptr; | ||
209 | 210 | ||
210 | storage = this_cpu_read(bpf_cgroup_storage[stype]); | 211 | storage = this_cpu_read(bpf_cgroup_storage[stype]); |
211 | 212 | ||
212 | return (unsigned long)&READ_ONCE(storage->buf)->data[0]; | 213 | if (stype == BPF_CGROUP_STORAGE_SHARED) |
214 | ptr = &READ_ONCE(storage->buf)->data[0]; | ||
215 | else | ||
216 | ptr = this_cpu_ptr(storage->percpu_buf); | ||
217 | |||
218 | return (unsigned long)ptr; | ||
213 | } | 219 | } |
214 | 220 | ||
215 | const struct bpf_func_proto bpf_get_local_storage_proto = { | 221 | const struct bpf_func_proto bpf_get_local_storage_proto = { |
diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c index 6742292fb39e..944eb297465f 100644 --- a/kernel/bpf/local_storage.c +++ b/kernel/bpf/local_storage.c | |||
@@ -152,6 +152,71 @@ static int cgroup_storage_update_elem(struct bpf_map *map, void *_key, | |||
152 | return 0; | 152 | return 0; |
153 | } | 153 | } |
154 | 154 | ||
155 | int bpf_percpu_cgroup_storage_copy(struct bpf_map *_map, void *_key, | ||
156 | void *value) | ||
157 | { | ||
158 | struct bpf_cgroup_storage_map *map = map_to_storage(_map); | ||
159 | struct bpf_cgroup_storage_key *key = _key; | ||
160 | struct bpf_cgroup_storage *storage; | ||
161 | int cpu, off = 0; | ||
162 | u32 size; | ||
163 | |||
164 | rcu_read_lock(); | ||
165 | storage = cgroup_storage_lookup(map, key, false); | ||
166 | if (!storage) { | ||
167 | rcu_read_unlock(); | ||
168 | return -ENOENT; | ||
169 | } | ||
170 | |||
171 | /* per_cpu areas are zero-filled and bpf programs can only | ||
172 | * access 'value_size' of them, so copying rounded areas | ||
173 | * will not leak any kernel data | ||
174 | */ | ||
175 | size = round_up(_map->value_size, 8); | ||
176 | for_each_possible_cpu(cpu) { | ||
177 | bpf_long_memcpy(value + off, | ||
178 | per_cpu_ptr(storage->percpu_buf, cpu), size); | ||
179 | off += size; | ||
180 | } | ||
181 | rcu_read_unlock(); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | int bpf_percpu_cgroup_storage_update(struct bpf_map *_map, void *_key, | ||
186 | void *value, u64 map_flags) | ||
187 | { | ||
188 | struct bpf_cgroup_storage_map *map = map_to_storage(_map); | ||
189 | struct bpf_cgroup_storage_key *key = _key; | ||
190 | struct bpf_cgroup_storage *storage; | ||
191 | int cpu, off = 0; | ||
192 | u32 size; | ||
193 | |||
194 | if (map_flags != BPF_ANY && map_flags != BPF_EXIST) | ||
195 | return -EINVAL; | ||
196 | |||
197 | rcu_read_lock(); | ||
198 | storage = cgroup_storage_lookup(map, key, false); | ||
199 | if (!storage) { | ||
200 | rcu_read_unlock(); | ||
201 | return -ENOENT; | ||
202 | } | ||
203 | |||
204 | /* the user space will provide round_up(value_size, 8) bytes that | ||
205 | * will be copied into per-cpu area. bpf programs can only access | ||
206 | * value_size of it. During lookup the same extra bytes will be | ||
207 | * returned or zeros which were zero-filled by percpu_alloc, | ||
208 | * so no kernel data leaks possible | ||
209 | */ | ||
210 | size = round_up(_map->value_size, 8); | ||
211 | for_each_possible_cpu(cpu) { | ||
212 | bpf_long_memcpy(per_cpu_ptr(storage->percpu_buf, cpu), | ||
213 | value + off, size); | ||
214 | off += size; | ||
215 | } | ||
216 | rcu_read_unlock(); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
155 | static int cgroup_storage_get_next_key(struct bpf_map *_map, void *_key, | 220 | static int cgroup_storage_get_next_key(struct bpf_map *_map, void *_key, |
156 | void *_next_key) | 221 | void *_next_key) |
157 | { | 222 | { |
@@ -287,60 +352,105 @@ void bpf_cgroup_storage_release(struct bpf_prog *prog, struct bpf_map *_map) | |||
287 | spin_unlock_bh(&map->lock); | 352 | spin_unlock_bh(&map->lock); |
288 | } | 353 | } |
289 | 354 | ||
355 | static size_t bpf_cgroup_storage_calculate_size(struct bpf_map *map, u32 *pages) | ||
356 | { | ||
357 | size_t size; | ||
358 | |||
359 | if (cgroup_storage_type(map) == BPF_CGROUP_STORAGE_SHARED) { | ||
360 | size = sizeof(struct bpf_storage_buffer) + map->value_size; | ||
361 | *pages = round_up(sizeof(struct bpf_cgroup_storage) + size, | ||
362 | PAGE_SIZE) >> PAGE_SHIFT; | ||
363 | } else { | ||
364 | size = map->value_size; | ||
365 | *pages = round_up(round_up(size, 8) * num_possible_cpus(), | ||
366 | PAGE_SIZE) >> PAGE_SHIFT; | ||
367 | } | ||
368 | |||
369 | return size; | ||
370 | } | ||
371 | |||
290 | struct bpf_cgroup_storage *bpf_cgroup_storage_alloc(struct bpf_prog *prog, | 372 | struct bpf_cgroup_storage *bpf_cgroup_storage_alloc(struct bpf_prog *prog, |
291 | enum bpf_cgroup_storage_type stype) | 373 | enum bpf_cgroup_storage_type stype) |
292 | { | 374 | { |
293 | struct bpf_cgroup_storage *storage; | 375 | struct bpf_cgroup_storage *storage; |
294 | struct bpf_map *map; | 376 | struct bpf_map *map; |
377 | gfp_t flags; | ||
378 | size_t size; | ||
295 | u32 pages; | 379 | u32 pages; |
296 | 380 | ||
297 | map = prog->aux->cgroup_storage[stype]; | 381 | map = prog->aux->cgroup_storage[stype]; |
298 | if (!map) | 382 | if (!map) |
299 | return NULL; | 383 | return NULL; |
300 | 384 | ||
301 | pages = round_up(sizeof(struct bpf_cgroup_storage) + | 385 | size = bpf_cgroup_storage_calculate_size(map, &pages); |
302 | sizeof(struct bpf_storage_buffer) + | 386 | |
303 | map->value_size, PAGE_SIZE) >> PAGE_SHIFT; | ||
304 | if (bpf_map_charge_memlock(map, pages)) | 387 | if (bpf_map_charge_memlock(map, pages)) |
305 | return ERR_PTR(-EPERM); | 388 | return ERR_PTR(-EPERM); |
306 | 389 | ||
307 | storage = kmalloc_node(sizeof(struct bpf_cgroup_storage), | 390 | storage = kmalloc_node(sizeof(struct bpf_cgroup_storage), |
308 | __GFP_ZERO | GFP_USER, map->numa_node); | 391 | __GFP_ZERO | GFP_USER, map->numa_node); |
309 | if (!storage) { | 392 | if (!storage) |
310 | bpf_map_uncharge_memlock(map, pages); | 393 | goto enomem; |
311 | return ERR_PTR(-ENOMEM); | ||
312 | } | ||
313 | 394 | ||
314 | storage->buf = kmalloc_node(sizeof(struct bpf_storage_buffer) + | 395 | flags = __GFP_ZERO | GFP_USER; |
315 | map->value_size, __GFP_ZERO | GFP_USER, | 396 | |
316 | map->numa_node); | 397 | if (stype == BPF_CGROUP_STORAGE_SHARED) { |
317 | if (!storage->buf) { | 398 | storage->buf = kmalloc_node(size, flags, map->numa_node); |
318 | bpf_map_uncharge_memlock(map, pages); | 399 | if (!storage->buf) |
319 | kfree(storage); | 400 | goto enomem; |
320 | return ERR_PTR(-ENOMEM); | 401 | } else { |
402 | storage->percpu_buf = __alloc_percpu_gfp(size, 8, flags); | ||
403 | if (!storage->percpu_buf) | ||
404 | goto enomem; | ||
321 | } | 405 | } |
322 | 406 | ||
323 | storage->map = (struct bpf_cgroup_storage_map *)map; | 407 | storage->map = (struct bpf_cgroup_storage_map *)map; |
324 | 408 | ||
325 | return storage; | 409 | return storage; |
410 | |||
411 | enomem: | ||
412 | bpf_map_uncharge_memlock(map, pages); | ||
413 | kfree(storage); | ||
414 | return ERR_PTR(-ENOMEM); | ||
415 | } | ||
416 | |||
417 | static void free_shared_cgroup_storage_rcu(struct rcu_head *rcu) | ||
418 | { | ||
419 | struct bpf_cgroup_storage *storage = | ||
420 | container_of(rcu, struct bpf_cgroup_storage, rcu); | ||
421 | |||
422 | kfree(storage->buf); | ||
423 | kfree(storage); | ||
424 | } | ||
425 | |||
426 | static void free_percpu_cgroup_storage_rcu(struct rcu_head *rcu) | ||
427 | { | ||
428 | struct bpf_cgroup_storage *storage = | ||
429 | container_of(rcu, struct bpf_cgroup_storage, rcu); | ||
430 | |||
431 | free_percpu(storage->percpu_buf); | ||
432 | kfree(storage); | ||
326 | } | 433 | } |
327 | 434 | ||
328 | void bpf_cgroup_storage_free(struct bpf_cgroup_storage *storage) | 435 | void bpf_cgroup_storage_free(struct bpf_cgroup_storage *storage) |
329 | { | 436 | { |
330 | u32 pages; | 437 | enum bpf_cgroup_storage_type stype; |
331 | struct bpf_map *map; | 438 | struct bpf_map *map; |
439 | u32 pages; | ||
332 | 440 | ||
333 | if (!storage) | 441 | if (!storage) |
334 | return; | 442 | return; |
335 | 443 | ||
336 | map = &storage->map->map; | 444 | map = &storage->map->map; |
337 | pages = round_up(sizeof(struct bpf_cgroup_storage) + | 445 | |
338 | sizeof(struct bpf_storage_buffer) + | 446 | bpf_cgroup_storage_calculate_size(map, &pages); |
339 | map->value_size, PAGE_SIZE) >> PAGE_SHIFT; | ||
340 | bpf_map_uncharge_memlock(map, pages); | 447 | bpf_map_uncharge_memlock(map, pages); |
341 | 448 | ||
342 | kfree_rcu(storage->buf, rcu); | 449 | stype = cgroup_storage_type(map); |
343 | kfree_rcu(storage, rcu); | 450 | if (stype == BPF_CGROUP_STORAGE_SHARED) |
451 | call_rcu(&storage->rcu, free_shared_cgroup_storage_rcu); | ||
452 | else | ||
453 | call_rcu(&storage->rcu, free_percpu_cgroup_storage_rcu); | ||
344 | } | 454 | } |
345 | 455 | ||
346 | void bpf_cgroup_storage_link(struct bpf_cgroup_storage *storage, | 456 | void bpf_cgroup_storage_link(struct bpf_cgroup_storage *storage, |
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8c91d2b41b1e..5742df21598c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c | |||
@@ -686,7 +686,8 @@ static int map_lookup_elem(union bpf_attr *attr) | |||
686 | 686 | ||
687 | if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || | 687 | if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || |
688 | map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || | 688 | map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || |
689 | map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) | 689 | map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY || |
690 | map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) | ||
690 | value_size = round_up(map->value_size, 8) * num_possible_cpus(); | 691 | value_size = round_up(map->value_size, 8) * num_possible_cpus(); |
691 | else if (IS_FD_MAP(map)) | 692 | else if (IS_FD_MAP(map)) |
692 | value_size = sizeof(u32); | 693 | value_size = sizeof(u32); |
@@ -705,6 +706,8 @@ static int map_lookup_elem(union bpf_attr *attr) | |||
705 | err = bpf_percpu_hash_copy(map, key, value); | 706 | err = bpf_percpu_hash_copy(map, key, value); |
706 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { | 707 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { |
707 | err = bpf_percpu_array_copy(map, key, value); | 708 | err = bpf_percpu_array_copy(map, key, value); |
709 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { | ||
710 | err = bpf_percpu_cgroup_storage_copy(map, key, value); | ||
708 | } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { | 711 | } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { |
709 | err = bpf_stackmap_copy(map, key, value); | 712 | err = bpf_stackmap_copy(map, key, value); |
710 | } else if (IS_FD_ARRAY(map)) { | 713 | } else if (IS_FD_ARRAY(map)) { |
@@ -774,7 +777,8 @@ static int map_update_elem(union bpf_attr *attr) | |||
774 | 777 | ||
775 | if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || | 778 | if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || |
776 | map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || | 779 | map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || |
777 | map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) | 780 | map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY || |
781 | map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) | ||
778 | value_size = round_up(map->value_size, 8) * num_possible_cpus(); | 782 | value_size = round_up(map->value_size, 8) * num_possible_cpus(); |
779 | else | 783 | else |
780 | value_size = map->value_size; | 784 | value_size = map->value_size; |
@@ -809,6 +813,9 @@ static int map_update_elem(union bpf_attr *attr) | |||
809 | err = bpf_percpu_hash_update(map, key, value, attr->flags); | 813 | err = bpf_percpu_hash_update(map, key, value, attr->flags); |
810 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { | 814 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { |
811 | err = bpf_percpu_array_update(map, key, value, attr->flags); | 815 | err = bpf_percpu_array_update(map, key, value, attr->flags); |
816 | } else if (map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) { | ||
817 | err = bpf_percpu_cgroup_storage_update(map, key, value, | ||
818 | attr->flags); | ||
812 | } else if (IS_FD_ARRAY(map)) { | 819 | } else if (IS_FD_ARRAY(map)) { |
813 | rcu_read_lock(); | 820 | rcu_read_lock(); |
814 | err = bpf_fd_array_map_update_elem(map, f.file, key, value, | 821 | err = bpf_fd_array_map_update_elem(map, f.file, key, value, |
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e90899df585d..a8cc83a970d1 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -2074,6 +2074,7 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, | |||
2074 | goto error; | 2074 | goto error; |
2075 | break; | 2075 | break; |
2076 | case BPF_MAP_TYPE_CGROUP_STORAGE: | 2076 | case BPF_MAP_TYPE_CGROUP_STORAGE: |
2077 | case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: | ||
2077 | if (func_id != BPF_FUNC_get_local_storage) | 2078 | if (func_id != BPF_FUNC_get_local_storage) |
2078 | goto error; | 2079 | goto error; |
2079 | break; | 2080 | break; |
@@ -2164,7 +2165,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, | |||
2164 | goto error; | 2165 | goto error; |
2165 | break; | 2166 | break; |
2166 | case BPF_FUNC_get_local_storage: | 2167 | case BPF_FUNC_get_local_storage: |
2167 | if (map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE) | 2168 | if (map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE && |
2169 | map->map_type != BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE) | ||
2168 | goto error; | 2170 | goto error; |
2169 | break; | 2171 | break; |
2170 | case BPF_FUNC_sk_select_reuseport: | 2172 | case BPF_FUNC_sk_select_reuseport: |
@@ -5049,6 +5051,12 @@ static int check_map_prog_compatibility(struct bpf_verifier_env *env, | |||
5049 | return 0; | 5051 | return 0; |
5050 | } | 5052 | } |
5051 | 5053 | ||
5054 | static bool bpf_map_is_cgroup_storage(struct bpf_map *map) | ||
5055 | { | ||
5056 | return (map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE || | ||
5057 | map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE); | ||
5058 | } | ||
5059 | |||
5052 | /* look for pseudo eBPF instructions that access map FDs and | 5060 | /* look for pseudo eBPF instructions that access map FDs and |
5053 | * replace them with actual map pointers | 5061 | * replace them with actual map pointers |
5054 | */ | 5062 | */ |
@@ -5139,10 +5147,9 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) | |||
5139 | } | 5147 | } |
5140 | env->used_maps[env->used_map_cnt++] = map; | 5148 | env->used_maps[env->used_map_cnt++] = map; |
5141 | 5149 | ||
5142 | if (map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE && | 5150 | if (bpf_map_is_cgroup_storage(map) && |
5143 | bpf_cgroup_storage_assign(env->prog, map)) { | 5151 | bpf_cgroup_storage_assign(env->prog, map)) { |
5144 | verbose(env, | 5152 | verbose(env, "only one cgroup storage of each type is allowed\n"); |
5145 | "only one cgroup storage is allowed\n"); | ||
5146 | fdput(f); | 5153 | fdput(f); |
5147 | return -EBUSY; | 5154 | return -EBUSY; |
5148 | } | 5155 | } |