aboutsummaryrefslogtreecommitdiffstats
path: root/common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch')
-rw-r--r--common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch274
1 files changed, 0 insertions, 274 deletions
diff --git a/common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch b/common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch
deleted file mode 100644
index 8f746ad1..00000000
--- a/common/recipes-kernel/linux/linux-yocto-4.9.21/0045-bpf-prevent-out-of-bounds-speculation.patch
+++ /dev/null
@@ -1,274 +0,0 @@
-From cc702f102892308be35e4f0dc52519a9c62c3fdc Mon Sep 17 00:00:00 2001
-From: Alexei Starovoitov <ast@kernel.org>
-Date: Sun, 7 Jan 2018 17:33:02 -0800
-Subject: [PATCH 045/103] bpf: prevent out-of-bounds speculation
-
-commit b2157399cc9898260d6031c5bfe45fe137c1fbe7 upstream.
-
-Under speculation, CPUs may mis-predict branches in bounds checks. Thus,
-memory accesses under a bounds check may be speculated even if the
-bounds check fails, providing a primitive for building a side channel.
-
-To avoid leaking kernel data round up array-based maps and mask the index
-after bounds check, so speculated load with out of bounds index will load
-either valid value from the array or zero from the padded area.
-
-Unconditionally mask index for all array types even when max_entries
-are not rounded to power of 2 for root user.
-When map is created by unpriv user generate a sequence of bpf insns
-that includes AND operation to make sure that JITed code includes
-the same 'index & index_mask' operation.
-
-If prog_array map is created by unpriv user replace
- bpf_tail_call(ctx, map, index);
-with
- if (index >= max_entries) {
- index &= map->index_mask;
- bpf_tail_call(ctx, map, index);
- }
-(along with roundup to power 2) to prevent out-of-bounds speculation.
-There is secondary redundant 'if (index >= max_entries)' in the interpreter
-and in all JITs, but they can be optimized later if necessary.
-
-Other array-like maps (cpumap, devmap, sockmap, perf_event_array, cgroup_array)
-cannot be used by unpriv, so no changes there.
-
-That fixes bpf side of "Variant 1: bounds check bypass (CVE-2017-5753)" on
-all architectures with and without JIT.
-
-v2->v3:
-Daniel noticed that attack potentially can be crafted via syscall commands
-without loading the program, so add masking to those paths as well.
-
-Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-Acked-by: John Fastabend <john.fastabend@gmail.com>
-Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-Cc: Jiri Slaby <jslaby@suse.cz>
-[ Backported to 4.9 - gregkh ]
-Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----
- include/linux/bpf.h | 2 ++
- include/linux/bpf_verifier.h | 6 +++++-
- kernel/bpf/arraymap.c | 31 ++++++++++++++++++++++---------
- kernel/bpf/verifier.c | 42 +++++++++++++++++++++++++++++++++++++++---
- 4 files changed, 68 insertions(+), 13 deletions(-)
-
-diff --git a/include/linux/bpf.h b/include/linux/bpf.h
-index c201017..0dbb21b 100644
---- a/include/linux/bpf.h
-+++ b/include/linux/bpf.h
-@@ -43,6 +43,7 @@ struct bpf_map {
- u32 max_entries;
- u32 map_flags;
- u32 pages;
-+ bool unpriv_array;
- struct user_struct *user;
- const struct bpf_map_ops *ops;
- struct work_struct work;
-@@ -189,6 +190,7 @@ struct bpf_prog_aux {
- struct bpf_array {
- struct bpf_map map;
- u32 elem_size;
-+ u32 index_mask;
- /* 'ownership' of prog_array is claimed by the first program that
- * is going to use this map or by the first program which FD is stored
- * in the map to make sure that all callers and callees have the same
-diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
-index a13b031..2edf8de 100644
---- a/include/linux/bpf_verifier.h
-+++ b/include/linux/bpf_verifier.h
-@@ -66,7 +66,11 @@ struct bpf_verifier_state_list {
- };
-
- struct bpf_insn_aux_data {
-- enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
-+ union {
-+ enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
-+ struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */
-+ };
-+ bool seen; /* this insn was processed by the verifier */
- };
-
- #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
-diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
-index a2ac051..eeb7f1b 100644
---- a/kernel/bpf/arraymap.c
-+++ b/kernel/bpf/arraymap.c
-@@ -47,9 +47,10 @@ static int bpf_array_alloc_percpu(struct bpf_array *array)
- static struct bpf_map *array_map_alloc(union bpf_attr *attr)
- {
- bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
-+ u32 elem_size, index_mask, max_entries;
-+ bool unpriv = !capable(CAP_SYS_ADMIN);
- struct bpf_array *array;
- u64 array_size;
-- u32 elem_size;
-
- /* check sanity of attributes */
- if (attr->max_entries == 0 || attr->key_size != 4 ||
-@@ -64,11 +65,20 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
-
- elem_size = round_up(attr->value_size, 8);
-
-+ max_entries = attr->max_entries;
-+ index_mask = roundup_pow_of_two(max_entries) - 1;
-+
-+ if (unpriv)
-+ /* round up array size to nearest power of 2,
-+ * since cpu will speculate within index_mask limits
-+ */
-+ max_entries = index_mask + 1;
-+
- array_size = sizeof(*array);
- if (percpu)
-- array_size += (u64) attr->max_entries * sizeof(void *);
-+ array_size += (u64) max_entries * sizeof(void *);
- else
-- array_size += (u64) attr->max_entries * elem_size;
-+ array_size += (u64) max_entries * elem_size;
-
- /* make sure there is no u32 overflow later in round_up() */
- if (array_size >= U32_MAX - PAGE_SIZE)
-@@ -82,6 +92,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr)
- if (!array)
- return ERR_PTR(-ENOMEM);
- }
-+ array->index_mask = index_mask;
-+ array->map.unpriv_array = unpriv;
-
- /* copy mandatory map attributes */
- array->map.map_type = attr->map_type;
-@@ -115,7 +127,7 @@ static void *array_map_lookup_elem(struct bpf_map *map, void *key)
- if (unlikely(index >= array->map.max_entries))
- return NULL;
-
-- return array->value + array->elem_size * index;
-+ return array->value + array->elem_size * (index & array->index_mask);
- }
-
- /* Called from eBPF program */
-@@ -127,7 +139,7 @@ static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key)
- if (unlikely(index >= array->map.max_entries))
- return NULL;
-
-- return this_cpu_ptr(array->pptrs[index]);
-+ return this_cpu_ptr(array->pptrs[index & array->index_mask]);
- }
-
- int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value)
-@@ -147,7 +159,7 @@ int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value)
- */
- size = round_up(map->value_size, 8);
- rcu_read_lock();
-- pptr = array->pptrs[index];
-+ pptr = array->pptrs[index & array->index_mask];
- for_each_possible_cpu(cpu) {
- bpf_long_memcpy(value + off, per_cpu_ptr(pptr, cpu), size);
- off += size;
-@@ -195,10 +207,11 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
- return -EEXIST;
-
- if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
-- memcpy(this_cpu_ptr(array->pptrs[index]),
-+ memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),
- value, map->value_size);
- else
-- memcpy(array->value + array->elem_size * index,
-+ memcpy(array->value +
-+ array->elem_size * (index & array->index_mask),
- value, map->value_size);
- return 0;
- }
-@@ -232,7 +245,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
- */
- size = round_up(map->value_size, 8);
- rcu_read_lock();
-- pptr = array->pptrs[index];
-+ pptr = array->pptrs[index & array->index_mask];
- for_each_possible_cpu(cpu) {
- bpf_long_memcpy(per_cpu_ptr(pptr, cpu), value + off, size);
- off += size;
-diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
-index 3f24718..787b851 100644
---- a/kernel/bpf/verifier.c
-+++ b/kernel/bpf/verifier.c
-@@ -1165,7 +1165,7 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env)
- }
- }
-
--static int check_call(struct bpf_verifier_env *env, int func_id)
-+static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
- {
- struct bpf_verifier_state *state = &env->cur_state;
- const struct bpf_func_proto *fn = NULL;
-@@ -1216,6 +1216,13 @@ static int check_call(struct bpf_verifier_env *env, int func_id)
- err = check_func_arg(env, BPF_REG_2, fn->arg2_type, &meta);
- if (err)
- return err;
-+ if (func_id == BPF_FUNC_tail_call) {
-+ if (meta.map_ptr == NULL) {
-+ verbose("verifier bug\n");
-+ return -EINVAL;
-+ }
-+ env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr;
-+ }
- err = check_func_arg(env, BPF_REG_3, fn->arg3_type, &meta);
- if (err)
- return err;
-@@ -2799,7 +2806,7 @@ static int do_check(struct bpf_verifier_env *env)
- return -EINVAL;
- }
-
-- err = check_call(env, insn->imm);
-+ err = check_call(env, insn->imm, insn_idx);
- if (err)
- return err;
-
-@@ -3129,7 +3136,11 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
- struct bpf_insn *insn = prog->insnsi;
- const struct bpf_func_proto *fn;
- const int insn_cnt = prog->len;
-- int i;
-+ struct bpf_insn insn_buf[16];
-+ struct bpf_prog *new_prog;
-+ struct bpf_map *map_ptr;
-+ int i, cnt, delta = 0;
-+
-
- for (i = 0; i < insn_cnt; i++, insn++) {
- if (insn->code != (BPF_JMP | BPF_CALL))
-@@ -3147,6 +3158,31 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
- */
- insn->imm = 0;
- insn->code |= BPF_X;
-+
-+ /* instead of changing every JIT dealing with tail_call
-+ * emit two extra insns:
-+ * if (index >= max_entries) goto out;
-+ * index &= array->index_mask;
-+ * to avoid out-of-bounds cpu speculation
-+ */
-+ map_ptr = env->insn_aux_data[i + delta].map_ptr;
-+ if (!map_ptr->unpriv_array)
-+ continue;
-+ insn_buf[0] = BPF_JMP_IMM(BPF_JGE, BPF_REG_3,
-+ map_ptr->max_entries, 2);
-+ insn_buf[1] = BPF_ALU32_IMM(BPF_AND, BPF_REG_3,
-+ container_of(map_ptr,
-+ struct bpf_array,
-+ map)->index_mask);
-+ insn_buf[2] = *insn;
-+ cnt = 3;
-+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-+ if (!new_prog)
-+ return -ENOMEM;
-+
-+ delta += cnt - 1;
-+ env->prog = prog = new_prog;
-+ insn = new_prog->insnsi + i + delta;
- continue;
- }
-
---
-2.7.4
-