CVE-2016-4557 bpf: Use after free vulnerability via double fdput
CVE-2016-4558 bpf: refcnt overflow (rhbz 1334307 1334303 1334311)
This commit is contained in:
parent
b4ebd48644
commit
a8751b1bcf
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue