diff options
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.patch | 274 |
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 - |