diff options
author | Josh Boyer <jwboyer@fedoraproject.org> | 2016-05-09 08:36:44 -0400 |
---|---|---|
committer | Josh Boyer <jwboyer@fedoraproject.org> | 2016-05-09 08:36:54 -0400 |
commit | a8751b1bcf8c78d6ab81777f01b1e378692878a7 (patch) | |
tree | 5756b55aae33478df2d5dcc8dac3f90094bd8f0c /bpf-fix-refcnt-overflow.patch | |
parent | b4ebd4864417f84e250552ff7b60176daf81709c (diff) | |
download | kernel-a8751b1bcf8c78d6ab81777f01b1e378692878a7.tar.gz kernel-a8751b1bcf8c78d6ab81777f01b1e378692878a7.tar.xz kernel-a8751b1bcf8c78d6ab81777f01b1e378692878a7.zip |
CVE-2016-4557 bpf: Use after free vulnerability via double fdput
CVE-2016-4558 bpf: refcnt overflow (rhbz 1334307 1334303 1334311)
Diffstat (limited to 'bpf-fix-refcnt-overflow.patch')
-rw-r--r-- | bpf-fix-refcnt-overflow.patch | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/bpf-fix-refcnt-overflow.patch b/bpf-fix-refcnt-overflow.patch new file mode 100644 index 000000000..1143c8286 --- /dev/null +++ b/bpf-fix-refcnt-overflow.patch @@ -0,0 +1,158 @@ +From 86db8dac9286f8397434184a6b442b6419e54ec0 Mon Sep 17 00:00:00 2001 +From: Alexei Starovoitov <ast@fb.com> +Date: Wed, 27 Apr 2016 18:56:20 -0700 +Subject: [PATCH] bpf: fix refcnt overflow + +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> +--- + 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(-) + +diff --git a/include/linux/bpf.h b/include/linux/bpf.h +index 83d1926c61e4..67bc2da5d233 100644 +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -165,12 +165,13 @@ void bpf_register_prog_type(struct bpf_prog_type_list *tl); + 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); + +diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c +index 5a8a797d50b7..d1a7646f79c5 100644 +--- a/kernel/bpf/inode.c ++++ b/kernel/bpf/inode.c +@@ -31,10 +31,10 @@ static void *bpf_any_get(void *raw, enum bpf_type type) + { + 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 filename *pathname, + 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; +diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c +index 3b39550d8485..4e32cc94edd9 100644 +--- a/kernel/bpf/syscall.c ++++ b/kernel/bpf/syscall.c +@@ -181,11 +181,18 @@ struct bpf_map *__bpf_map_get(struct fd f) + 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(u32 ufd) + 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(struct fd f) + 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; +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 2e7f7ab739e4..060e4c4c37ea 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2023,15 +2023,18 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env) + 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++; +-- +2.5.5 + |