[linux] 01/01: Add bpf security fixes
debian-kernel at lists.debian.org
debian-kernel at lists.debian.org
Thu May 5 22:14:07 UTC 2016
This is an automated email from the git hooks/post-receive script.
benh pushed a commit to branch sid
in repository linux.
commit 405645d78889b5effdcfbcc0d9ef6ba75a3ac40d
Author: Ben Hutchings <ben at decadent.org.uk>
Date: Thu May 5 23:13:51 2016 +0100
Add bpf security fixes
---
debian/changelog | 3 +
...pf-fix-check_map_func_compatibility-logic.patch | 94 +++++++++++++
...uble-fdput-in-replace_map_fd_with_map_ptr.patch | 41 ++++++
.../bugfix/all/bpf-fix-refcnt-overflow.patch | 147 +++++++++++++++++++++
debian/patches/series | 3 +
5 files changed, 288 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 67ca9af..919e3eb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -7,6 +7,9 @@ linux (4.5.2-2) UNRELEASED; urgency=medium
* bug control: Update list of related firmware packages
* Revert "sp5100_tco: fix the device check for SB800 and later chipsets"
(Closes: #823146; probably fixes #822651)
+ * bpf: fix double-fdput in replace_map_fd_with_map_ptr() (CVE-2016-XXXX)
+ * bpf: fix refcnt overflow (CVE-2016-XXXX)
+ * bpf: fix check_map_func_compatibility logic (CVE-2016-XXXX)
-- Uwe Kleine-König <ukleinek at debian.org> Sun, 01 May 2016 16:13:04 +0200
diff --git a/debian/patches/bugfix/all/bpf-fix-check_map_func_compatibility-logic.patch b/debian/patches/bugfix/all/bpf-fix-check_map_func_compatibility-logic.patch
new file mode 100644
index 0000000..83a0254
--- /dev/null
+++ b/debian/patches/bugfix/all/bpf-fix-check_map_func_compatibility-logic.patch
@@ -0,0 +1,94 @@
+From: Alexei Starovoitov <ast at fb.com>
+Date: Wed, 27 Apr 2016 18:56:21 -0700
+Subject: [3/3] bpf: fix check_map_func_compatibility logic
+Origin: https://git.kernel.org/linus/6aff67c85c9e5a4bc99e5211c1bac547936626ca
+
+The commit 35578d798400 ("bpf: Implement function bpf_perf_event_read() that get the selected hardware PMU conuter")
+introduced clever way to check bpf_helper<->map_type compatibility.
+Later on commit a43eec304259 ("bpf: introduce bpf_perf_event_output() helper") adjusted
+the logic and inadvertently broke it.
+Get rid of the clever bool compare and go back to two-way check
+from map and from helper perspective.
+
+Fixes: a43eec304259 ("bpf: introduce bpf_perf_event_output() helper")
+Reported-by: Jann Horn <jannh at google.com>
+Signed-off-by: Alexei Starovoitov <ast at kernel.org>
+Signed-off-by: Daniel Borkmann <daniel at iogearbox.net>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+[bwh: Backported to 4.5:
+ - Drop the STACK_TRACE case
+ - No verbose() logging]
+---
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -239,15 +239,6 @@ static const char * const reg_type_str[]
+ [CONST_IMM] = "imm",
+ };
+
+-static const struct {
+- int map_type;
+- int func_id;
+-} func_limit[] = {
+- {BPF_MAP_TYPE_PROG_ARRAY, BPF_FUNC_tail_call},
+- {BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_FUNC_perf_event_read},
+- {BPF_MAP_TYPE_PERF_EVENT_ARRAY, BPF_FUNC_perf_event_output},
+-};
+-
+ static void print_verifier_state(struct verifier_env *env)
+ {
+ enum bpf_reg_type t;
+@@ -898,24 +889,42 @@ static int check_func_arg(struct verifie
+
+ static int check_map_func_compatibility(struct bpf_map *map, int func_id)
+ {
+- bool bool_map, bool_func;
+- int i;
+-
+ if (!map)
+ return 0;
+
+- for (i = 0; i < ARRAY_SIZE(func_limit); i++) {
+- bool_map = (map->map_type == func_limit[i].map_type);
+- bool_func = (func_id == func_limit[i].func_id);
+- /* only when map & func pair match it can continue.
+- * don't allow any other map type to be passed into
+- * the special func;
+- */
+- if (bool_func && bool_map != bool_func)
+- return -EINVAL;
++ /* We need a two way check, first is from map perspective ... */
++ switch (map->map_type) {
++ case BPF_MAP_TYPE_PROG_ARRAY:
++ if (func_id != BPF_FUNC_tail_call)
++ goto error;
++ break;
++ case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
++ if (func_id != BPF_FUNC_perf_event_read &&
++ func_id != BPF_FUNC_perf_event_output)
++ goto error;
++ break;
++ default:
++ break;
++ }
++
++ /* ... and second from the function itself. */
++ switch (func_id) {
++ case BPF_FUNC_tail_call:
++ if (map->map_type != BPF_MAP_TYPE_PROG_ARRAY)
++ goto error;
++ break;
++ case BPF_FUNC_perf_event_read:
++ case BPF_FUNC_perf_event_output:
++ if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
++ goto error;
++ break;
++ default:
++ break;
+ }
+
+ return 0;
++error:
++ return -EINVAL;
+ }
+
+ static int check_call(struct verifier_env *env, int func_id)
diff --git a/debian/patches/bugfix/all/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch b/debian/patches/bugfix/all/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
new file mode 100644
index 0000000..4c43fcd
--- /dev/null
+++ b/debian/patches/bugfix/all/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
@@ -0,0 +1,41 @@
+From: Jann Horn <jannh at google.com>
+Date: Tue, 26 Apr 2016 22:26:26 +0200
+Subject: [1/3] bpf: fix double-fdput in replace_map_fd_with_map_ptr()
+Origin: https://git.kernel.org/linus/8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7
+
+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 at google.com>
+Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+Acked-by: Alexei Starovoitov <ast at kernel.org>
+Acked-by: Daniel Borkmann <daniel at iogearbox.net>
+Signed-off-by: David S. Miller <davem at davemloft.net>
+---
+ kernel/bpf/verifier.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -2003,7 +2003,6 @@ static int replace_map_fd_with_map_ptr(s
+ if (IS_ERR(map)) {
+ verbose("fd %d is not pointing to valid bpf_map\n",
+ insn->imm);
+- fdput(f);
+ return PTR_ERR(map);
+ }
+
diff --git a/debian/patches/bugfix/all/bpf-fix-refcnt-overflow.patch b/debian/patches/bugfix/all/bpf-fix-refcnt-overflow.patch
new file mode 100644
index 0000000..a5b3d77
--- /dev/null
+++ b/debian/patches/bugfix/all/bpf-fix-refcnt-overflow.patch
@@ -0,0 +1,147 @@
+From: Alexei Starovoitov <ast at fb.com>
+Date: Wed, 27 Apr 2016 18:56:20 -0700
+Subject: [2/3] bpf: fix refcnt overflow
+Origin: https://git.kernel.org/linus/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 at google.com>
+Signed-off-by: Alexei Starovoitov <ast at kernel.org>
+Acked-by: Daniel Borkmann <daniel at iogearbox.net>
+Signed-off-by: David S. Miller <davem at 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(-)
+
+--- 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);
+@@ -297,7 +297,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
+@@ -201,11 +201,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)
+@@ -217,7 +224,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;
+@@ -600,6 +607,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()
+ */
+@@ -612,7 +628,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
+@@ -2022,15 +2022,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++;
diff --git a/debian/patches/series b/debian/patches/series
index 91d8b4a..6211c5b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -155,3 +155,6 @@ bugfix/all/power-cpupower-fix-manpages-NAME.patch
bugfix/all/tools-lib-traceevent-fix-use-of-uninitialized-variables.patch
bugfix/all/scripts-fix-x.509-pem-support-in-sign-file.patch
bugfix/x86/revert-sp5100_tco-fix-the-device-check-for-SB800-and.patch
+bugfix/all/bpf-fix-double-fdput-in-replace_map_fd_with_map_ptr.patch
+bugfix/all/bpf-fix-refcnt-overflow.patch
+bugfix/all/bpf-fix-check_map_func_compatibility-logic.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git
More information about the Kernel-svn-changes
mailing list