| @@ -46,11 +46,38 @@ void bpf_register_map_type(struct bpf_map_type_list *tl) · 45 unmodified lines |
| 46 | 46 | list_add(&tl->list_node, &bpf_map_types); |
| 47 | 47 | } |
| 48 | 48 | |
| 49 | +static 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 | + |
| 67 | +static 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 | 75 | /* called from workqueue */ |
| 50 | 76 | static void bpf_map_free_deferred(struct work_struct *work) |
| 51 | 77 | { |
| 52 | 78 | struct bpf_map *map = container_of(work, struct bpf_map, work); |
| 53 | 79 | |
| 80 | + bpf_map_uncharge_memlock(map); |
| 54 | 81 | /* implementation dependent freeing */ |
| 55 | 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 | 138 | atomic_set(&map->refcnt, 1); |
| 112 | 139 | |
| 140 | + err = bpf_map_charge_memlock(map); |
| 141 | + if (err) |
| 142 | + goto free_map; |
| 143 | + |
| 113 | 144 | err = anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC); |
| 114 | 145 | |
| 115 | 146 | if (err < 0) |
| @@ -442,11 +473,37 @@ static void free_used_maps(struct bpf_prog_aux *aux) · 326 unmodified lines |
| 442 | 473 | kfree(aux->used_maps); |
| 443 | 474 | } |
| 444 | 475 | |
| 476 | +static 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 | + |
| 493 | +static 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 | + |
| 445 | 501 | static void __prog_put_rcu(struct rcu_head *rcu) |
| 446 | 502 | { |
| 447 | 503 | struct bpf_prog_aux *aux = container_of(rcu, struct bpf_prog_aux, rcu); |
| 448 | 504 | |
| 449 | 505 | free_used_maps(aux); |
| 506 | + bpf_prog_uncharge_memlock(aux->prog); |
| 450 | 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 | 611 | if (!prog) |
| 555 | 612 | return -ENOMEM; |
| 556 | 613 | |
| 614 | + err = bpf_prog_charge_memlock(prog); |
| 615 | + if (err) |
| 616 | + goto free_prog_nouncharge; |
| 617 | + |
| 557 | 618 | prog->len = attr->insn_cnt; |
| 558 | 619 | |
| 559 | 620 | err = -EFAULT; |
| @@ -595,6 +656,8 @@ static int bpf_prog_load(union bpf_attr *attr) · 35 unmodified lines |
| 595 | 656 | free_used_maps: |
| 596 | 657 | free_used_maps(prog->aux); |
| 597 | 658 | free_prog: |
| 659 | + bpf_prog_uncharge_memlock(prog); |
| 660 | +free_prog_nouncharge: |
| 598 | 661 | bpf_prog_free(prog); |
| 599 | 662 | return err; |
| 600 | 663 | } |