| From foo@baz Mon May 16 11:21:32 PDT 2016 |
| From: Alexei Starovoitov <ast@fb.com> |
| Date: Wed, 27 Apr 2016 18:56:20 -0700 |
| Subject: bpf: fix refcnt overflow |
| |
| From: Alexei Starovoitov <ast@fb.com> |
| |
| [ Upstream commit 92117d8443bc5afacc8d5ba82e541946310f106e ] |
| |
| On a system with >32Gbyte of phyiscal memory and infinite RLIMIT_MEMLOCK, |
| the malicious application may overflow 32-bit bpf program refcnt. |
| It's also possible to overflow map refcnt on 1Tb system. |
| Impose 32k hard limit which means that the same bpf program or |
| map cannot be shared by more than 32k processes. |
| |
| Fixes: 1be7f75d1668 ("bpf: enable non-root eBPF programs") |
| Reported-by: Jann Horn <jannh@google.com> |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| Acked-by: Daniel Borkmann <daniel@iogearbox.net> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/bpf.h | 3 ++- |
| kernel/bpf/inode.c | 7 ++++--- |
| kernel/bpf/syscall.c | 24 ++++++++++++++++++++---- |
| kernel/bpf/verifier.c | 11 +++++++---- |
| 4 files changed, 33 insertions(+), 12 deletions(-) |
| |
| --- a/include/linux/bpf.h |
| +++ b/include/linux/bpf.h |
| @@ -165,12 +165,13 @@ void bpf_register_prog_type(struct bpf_p |
| void bpf_register_map_type(struct bpf_map_type_list *tl); |
| |
| struct bpf_prog *bpf_prog_get(u32 ufd); |
| +struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog); |
| void bpf_prog_put(struct bpf_prog *prog); |
| void bpf_prog_put_rcu(struct bpf_prog *prog); |
| |
| struct bpf_map *bpf_map_get_with_uref(u32 ufd); |
| struct bpf_map *__bpf_map_get(struct fd f); |
| -void bpf_map_inc(struct bpf_map *map, bool uref); |
| +struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref); |
| void bpf_map_put_with_uref(struct bpf_map *map); |
| void bpf_map_put(struct bpf_map *map); |
| |
| --- a/kernel/bpf/inode.c |
| +++ b/kernel/bpf/inode.c |
| @@ -31,10 +31,10 @@ static void *bpf_any_get(void *raw, enum |
| { |
| switch (type) { |
| case BPF_TYPE_PROG: |
| - atomic_inc(&((struct bpf_prog *)raw)->aux->refcnt); |
| + raw = bpf_prog_inc(raw); |
| break; |
| case BPF_TYPE_MAP: |
| - bpf_map_inc(raw, true); |
| + raw = bpf_map_inc(raw, true); |
| break; |
| default: |
| WARN_ON_ONCE(1); |
| @@ -277,7 +277,8 @@ static void *bpf_obj_do_get(const struct |
| goto out; |
| |
| raw = bpf_any_get(inode->i_private, *type); |
| - touch_atime(&path); |
| + if (!IS_ERR(raw)) |
| + touch_atime(&path); |
| |
| path_put(&path); |
| return raw; |
| --- a/kernel/bpf/syscall.c |
| +++ b/kernel/bpf/syscall.c |
| @@ -181,11 +181,18 @@ struct bpf_map *__bpf_map_get(struct fd |
| return f.file->private_data; |
| } |
| |
| -void bpf_map_inc(struct bpf_map *map, bool uref) |
| +/* prog's and map's refcnt limit */ |
| +#define BPF_MAX_REFCNT 32768 |
| + |
| +struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref) |
| { |
| - atomic_inc(&map->refcnt); |
| + if (atomic_inc_return(&map->refcnt) > BPF_MAX_REFCNT) { |
| + atomic_dec(&map->refcnt); |
| + return ERR_PTR(-EBUSY); |
| + } |
| if (uref) |
| atomic_inc(&map->usercnt); |
| + return map; |
| } |
| |
| struct bpf_map *bpf_map_get_with_uref(u32 ufd) |
| @@ -197,7 +204,7 @@ struct bpf_map *bpf_map_get_with_uref(u3 |
| if (IS_ERR(map)) |
| return map; |
| |
| - bpf_map_inc(map, true); |
| + map = bpf_map_inc(map, true); |
| fdput(f); |
| |
| return map; |
| @@ -580,6 +587,15 @@ static struct bpf_prog *__bpf_prog_get(s |
| return f.file->private_data; |
| } |
| |
| +struct bpf_prog *bpf_prog_inc(struct bpf_prog *prog) |
| +{ |
| + if (atomic_inc_return(&prog->aux->refcnt) > BPF_MAX_REFCNT) { |
| + atomic_dec(&prog->aux->refcnt); |
| + return ERR_PTR(-EBUSY); |
| + } |
| + return prog; |
| +} |
| + |
| /* called by sockets/tracing/seccomp before attaching program to an event |
| * pairs with bpf_prog_put() |
| */ |
| @@ -592,7 +608,7 @@ struct bpf_prog *bpf_prog_get(u32 ufd) |
| if (IS_ERR(prog)) |
| return prog; |
| |
| - atomic_inc(&prog->aux->refcnt); |
| + prog = bpf_prog_inc(prog); |
| fdput(f); |
| |
| return prog; |
| --- a/kernel/bpf/verifier.c |
| +++ b/kernel/bpf/verifier.c |
| @@ -2023,15 +2023,18 @@ static int replace_map_fd_with_map_ptr(s |
| return -E2BIG; |
| } |
| |
| - /* remember this map */ |
| - env->used_maps[env->used_map_cnt++] = map; |
| - |
| /* hold the map. If the program is rejected by verifier, |
| * the map will be released by release_maps() or it |
| * will be used by the valid program until it's unloaded |
| * and all maps are released in free_bpf_prog_info() |
| */ |
| - bpf_map_inc(map, false); |
| + map = bpf_map_inc(map, false); |
| + if (IS_ERR(map)) { |
| + fdput(f); |
| + return PTR_ERR(map); |
| + } |
| + env->used_maps[env->used_map_cnt++] = map; |
| + |
| fdput(f); |
| next_insn: |
| insn++; |