summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Boyer <jwboyer@fedoraproject.org>2016-05-09 08:36:44 -0400
committerJosh Boyer <jwboyer@fedoraproject.org>2016-05-09 08:36:54 -0400
commita8751b1bcf8c78d6ab81777f01b1e378692878a7 (patch)
tree5756b55aae33478df2d5dcc8dac3f90094bd8f0c
parentb4ebd4864417f84e250552ff7b60176daf81709c (diff)
downloadkernel-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)
-rw-r--r--bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch46
-rw-r--r--bpf-fix-refcnt-overflow.patch158
-rw-r--r--kernel.spec8
3 files changed, 212 insertions, 0 deletions
diff --git a/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch b/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
new file mode 100644
index 000000000..3ba32bae7
--- /dev/null
+++ b/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
@@ -0,0 +1,46 @@
+From 8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7 Mon Sep 17 00:00:00 2001
+From: Jann Horn <jannh@google.com>
+Date: Tue, 26 Apr 2016 22:26:26 +0200
+Subject: [PATCH] bpf: fix double-fdput in replace_map_fd_with_map_ptr()
+
+When bpf(BPF_PROG_LOAD, ...) was invoked with a BPF program whose bytecode
+references a non-map file descriptor as a map file descriptor, the error
+handling code called fdput() twice instead of once (in __bpf_map_get() and
+in replace_map_fd_with_map_ptr()). If the file descriptor table of the
+current task is shared, this causes f_count to be decremented too much,
+allowing the struct file to be freed while it is still in use
+(use-after-free). This can be exploited to gain root privileges by an
+unprivileged user.
+
+This bug was introduced in
+commit 0246e64d9a5f ("bpf: handle pseudo BPF_LD_IMM64 insn"), but is only
+exploitable since
+commit 1be7f75d1668 ("bpf: enable non-root eBPF programs") because
+previously, CAP_SYS_ADMIN was required to reach the vulnerable code.
+
+(posted publicly according to request by maintainer)
+
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ kernel/bpf/verifier.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
+index 618ef77c302a..db2574e7b8b0 100644
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -2030,7 +2030,6 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)
+ if (IS_ERR(map)) {
+ verbose("fd %d is not pointing to valid bpf_map\n",
+ insn->imm);
+- fdput(f);
+ return PTR_ERR(map);
+ }
+
+--
+2.5.5
+
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
+
diff --git a/kernel.spec b/kernel.spec
index bb043ace9..ef87a9273 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -648,6 +648,10 @@ Patch707: net-fix-infoleak-in-rtnetlink.patch
#CVE-2016-xxxx rhbz 1333712 1333713
Patch708: propogate_mnt-Handle-the-first-propogated-copy-being.patch
+#CVE-2016-4557 CVE-2016-4558 rhbz 1334307 1334303 1334311
+Patch711: bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
+Patch712: bpf-fix-refcnt-overflow.patch
+
# END OF PATCH DEFINITIONS
%endif
@@ -2091,6 +2095,10 @@ fi
#
#
%changelog
+* Mon May 09 2016 Josh Boyer <jwboyer@fedoraproject.org>
+- CVE-2016-4557 bpf: Use after free vulnerability via double fdput
+ CVE-2016-4558 bpf: refcnt overflow (rhbz 1334307 1334303 1334311)
+
* Fri May 06 2016 Josh Boyer <jwboyer@fedoraproject.org>
- Oops in propogate_mnt if first copy is slave (rhbz 1333712 1333713)