All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net 0/3] bpf: fix several bugs
@ 2016-04-28  1:56 Alexei Starovoitov
  2016-04-28  1:56 ` [PATCH net 1/3] bpf: fix refcnt overflow Alexei Starovoitov
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Alexei Starovoitov @ 2016-04-28  1:56 UTC (permalink / raw)
  To: David S . Miller
  Cc: Daniel Borkmann, Jann Horn, Linus Torvalds, netdev, kernel-team

First two patches address bugs found by Jann Horn.
Last patch is a minor samples fix spotted during the testing.

Alexei Starovoitov (3):
  bpf: fix refcnt overflow
  bpf: fix check_map_func_compatibility logic
  samples/bpf: fix trace_output example

 include/linux/bpf.h             |  3 +-
 kernel/bpf/inode.c              |  7 ++--
 kernel/bpf/syscall.c            | 24 ++++++++++---
 kernel/bpf/verifier.c           | 76 +++++++++++++++++++++++++----------------
 samples/bpf/trace_output_kern.c |  1 -
 5 files changed, 73 insertions(+), 38 deletions(-)

-- 
2.8.0

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net 1/3] bpf: fix refcnt overflow
  2016-04-28  1:56 [PATCH net 0/3] bpf: fix several bugs Alexei Starovoitov
@ 2016-04-28  1:56 ` Alexei Starovoitov
  2016-04-28  1:56 ` [PATCH net 2/3] bpf: fix check_map_func_compatibility logic Alexei Starovoitov
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Alexei Starovoitov @ 2016-04-28  1:56 UTC (permalink / raw)
  To: David S . Miller
  Cc: Daniel Borkmann, Jann Horn, Linus Torvalds, netdev, kernel-team

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>
---
 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 21ee41b92e8a..f1d5c5acc8dd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -171,12 +171,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);
 int bpf_map_precharge_memlock(u32 pages);
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index f2ece3c174a5..8f94ca1860cf 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);
@@ -297,7 +297,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 adc5e4bd74f8..cf5e9f7ad13a 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -218,11 +218,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)
@@ -234,7 +241,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;
@@ -658,6 +665,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()
  */
@@ -670,7 +686,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 db2574e7b8b0..89bcaa0966da 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2049,15 +2049,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.8.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net 2/3] bpf: fix check_map_func_compatibility logic
  2016-04-28  1:56 [PATCH net 0/3] bpf: fix several bugs Alexei Starovoitov
  2016-04-28  1:56 ` [PATCH net 1/3] bpf: fix refcnt overflow Alexei Starovoitov
@ 2016-04-28  1:56 ` Alexei Starovoitov
  2016-04-28  9:31   ` Jann Horn
  2016-04-28  1:56 ` [PATCH net 3/3] samples/bpf: fix trace_output example Alexei Starovoitov
  2016-04-28 21:30 ` [PATCH net 0/3] bpf: fix several bugs David Miller
  3 siblings, 1 reply; 6+ messages in thread
From: Alexei Starovoitov @ 2016-04-28  1:56 UTC (permalink / raw)
  To: David S . Miller
  Cc: Daniel Borkmann, Jann Horn, Linus Torvalds, netdev, kernel-team

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@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
---
 kernel/bpf/verifier.c | 65 +++++++++++++++++++++++++++++++--------------------
 1 file changed, 40 insertions(+), 25 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 89bcaa0966da..c5c17a62f509 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -239,16 +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},
-	{BPF_MAP_TYPE_STACK_TRACE, BPF_FUNC_get_stackid},
-};
-
 static void print_verifier_state(struct verifier_env *env)
 {
 	enum bpf_reg_type t;
@@ -921,27 +911,52 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
 
 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) {
-			verbose("cannot pass map_type %d into func %d\n",
-				map->map_type, func_id);
-			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;
+	case BPF_MAP_TYPE_STACK_TRACE:
+		if (func_id != BPF_FUNC_get_stackid)
+			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;
+	case BPF_FUNC_get_stackid:
+		if (map->map_type != BPF_MAP_TYPE_STACK_TRACE)
+			goto error;
+		break;
+	default:
+		break;
 	}
 
 	return 0;
+error:
+	verbose("cannot pass map_type %d into func %d\n",
+		map->map_type, func_id);
+	return -EINVAL;
 }
 
 static int check_call(struct verifier_env *env, int func_id)
-- 
2.8.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH net 3/3] samples/bpf: fix trace_output example
  2016-04-28  1:56 [PATCH net 0/3] bpf: fix several bugs Alexei Starovoitov
  2016-04-28  1:56 ` [PATCH net 1/3] bpf: fix refcnt overflow Alexei Starovoitov
  2016-04-28  1:56 ` [PATCH net 2/3] bpf: fix check_map_func_compatibility logic Alexei Starovoitov
@ 2016-04-28  1:56 ` Alexei Starovoitov
  2016-04-28 21:30 ` [PATCH net 0/3] bpf: fix several bugs David Miller
  3 siblings, 0 replies; 6+ messages in thread
From: Alexei Starovoitov @ 2016-04-28  1:56 UTC (permalink / raw)
  To: David S . Miller
  Cc: Daniel Borkmann, Jann Horn, Linus Torvalds, netdev, kernel-team

llvm cannot always recognize memset as builtin function and optimize
it away, so just delete it. It was a leftover from testing
of bpf_perf_event_output() with large data structures.

Fixes: 39111695b1b8 ("samples: bpf: add bpf_perf_event_output example")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
 samples/bpf/trace_output_kern.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/samples/bpf/trace_output_kern.c b/samples/bpf/trace_output_kern.c
index 8d8d1ec429eb..9b96f4fb8cea 100644
--- a/samples/bpf/trace_output_kern.c
+++ b/samples/bpf/trace_output_kern.c
@@ -18,7 +18,6 @@ int bpf_prog1(struct pt_regs *ctx)
 		u64 cookie;
 	} data;
 
-	memset(&data, 0, sizeof(data));
 	data.pid = bpf_get_current_pid_tgid();
 	data.cookie = 0x12345678;
 
-- 
2.8.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net 2/3] bpf: fix check_map_func_compatibility logic
  2016-04-28  1:56 ` [PATCH net 2/3] bpf: fix check_map_func_compatibility logic Alexei Starovoitov
@ 2016-04-28  9:31   ` Jann Horn
  0 siblings, 0 replies; 6+ messages in thread
From: Jann Horn @ 2016-04-28  9:31 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David S . Miller, Daniel Borkmann, Linus Torvalds, netdev, kernel-team

On Thu, Apr 28, 2016 at 3:56 AM, Alexei Starovoitov <ast@fb.com> wrote:
> 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@google.com>
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---
>  kernel/bpf/verifier.c | 65 +++++++++++++++++++++++++++++++--------------------
>  1 file changed, 40 insertions(+), 25 deletions(-)
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 89bcaa0966da..c5c17a62f509 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[...]
> +       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;
> +       case BPF_MAP_TYPE_STACK_TRACE:
> +               if (func_id != BPF_FUNC_get_stackid)
> +                       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;
> +       case BPF_FUNC_get_stackid:
> +               if (map->map_type != BPF_MAP_TYPE_STACK_TRACE)
> +                       goto error;
> +               break;
> +       default:
> +               break;
>         }

Looks good to me.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net 0/3] bpf: fix several bugs
  2016-04-28  1:56 [PATCH net 0/3] bpf: fix several bugs Alexei Starovoitov
                   ` (2 preceding siblings ...)
  2016-04-28  1:56 ` [PATCH net 3/3] samples/bpf: fix trace_output example Alexei Starovoitov
@ 2016-04-28 21:30 ` David Miller
  3 siblings, 0 replies; 6+ messages in thread
From: David Miller @ 2016-04-28 21:30 UTC (permalink / raw)
  To: ast; +Cc: daniel, jannh, torvalds, netdev, kernel-team

From: Alexei Starovoitov <ast@fb.com>
Date: Wed, 27 Apr 2016 18:56:19 -0700

> First two patches address bugs found by Jann Horn.
> Last patch is a minor samples fix spotted during the testing.

Series applied and queued up for -stable, thanks.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2016-04-28 21:30 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-28  1:56 [PATCH net 0/3] bpf: fix several bugs Alexei Starovoitov
2016-04-28  1:56 ` [PATCH net 1/3] bpf: fix refcnt overflow Alexei Starovoitov
2016-04-28  1:56 ` [PATCH net 2/3] bpf: fix check_map_func_compatibility logic Alexei Starovoitov
2016-04-28  9:31   ` Jann Horn
2016-04-28  1:56 ` [PATCH net 3/3] samples/bpf: fix trace_output example Alexei Starovoitov
2016-04-28 21:30 ` [PATCH net 0/3] bpf: fix several bugs David Miller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.