]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Jan 2018 19:48:44 +0000 (20:48 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 Jan 2018 19:48:44 +0000 (20:48 +0100)
added patches:
bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch
bpf-prevent-out-of-bounds-speculation.patch

queue-4.9/bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch [new file with mode: 0644]
queue-4.9/bpf-prevent-out-of-bounds-speculation.patch [new file with mode: 0644]
queue-4.9/series

diff --git a/queue-4.9/bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch b/queue-4.9/bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch
new file mode 100644 (file)
index 0000000..3186e4c
--- /dev/null
@@ -0,0 +1,80 @@
+From bbeb6e4323dad9b5e0ee9f60c223dd532e2403b1 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Wed, 10 Jan 2018 23:25:05 +0100
+Subject: bpf, array: fix overflow in max_entries and undefined behavior in index_mask
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+commit bbeb6e4323dad9b5e0ee9f60c223dd532e2403b1 upstream.
+
+syzkaller tried to alloc a map with 0xfffffffd entries out of a userns,
+and thus unprivileged. With the recently added logic in b2157399cc98
+("bpf: prevent out-of-bounds speculation") we round this up to the next
+power of two value for max_entries for unprivileged such that we can
+apply proper masking into potentially zeroed out map slots.
+
+However, this will generate an index_mask of 0xffffffff, and therefore
+a + 1 will let this overflow into new max_entries of 0. This will pass
+allocation, etc, and later on map access we still enforce on the original
+attr->max_entries value which was 0xfffffffd, therefore triggering GPF
+all over the place. Thus bail out on overflow in such case.
+
+Moreover, on 32 bit archs roundup_pow_of_two() can also not be used,
+since fls_long(max_entries - 1) can result in 32 and 1UL << 32 in 32 bit
+space is undefined. Therefore, do this by hand in a 64 bit variable.
+
+This fixes all the issues triggered by syzkaller's reproducers.
+
+Fixes: b2157399cc98 ("bpf: prevent out-of-bounds speculation")
+Reported-by: syzbot+b0efb8e572d01bce1ae0@syzkaller.appspotmail.com
+Reported-by: syzbot+6c15e9744f75f2364773@syzkaller.appspotmail.com
+Reported-by: syzbot+d2f5524fb46fd3b312ee@syzkaller.appspotmail.com
+Reported-by: syzbot+61d23c95395cc90dbc2b@syzkaller.appspotmail.com
+Reported-by: syzbot+0d363c942452cca68c01@syzkaller.appspotmail.com
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ kernel/bpf/arraymap.c |   18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/kernel/bpf/arraymap.c
++++ b/kernel/bpf/arraymap.c
+@@ -49,7 +49,7 @@ static struct bpf_map *array_map_alloc(u
+       u32 elem_size, index_mask, max_entries;
+       bool unpriv = !capable(CAP_SYS_ADMIN);
+       struct bpf_array *array;
+-      u64 array_size;
++      u64 array_size, mask64;
+       /* check sanity of attributes */
+       if (attr->max_entries == 0 || attr->key_size != 4 ||
+@@ -65,13 +65,25 @@ static struct bpf_map *array_map_alloc(u
+       elem_size = round_up(attr->value_size, 8);
+       max_entries = attr->max_entries;
+-      index_mask = roundup_pow_of_two(max_entries) - 1;
+-      if (unpriv)
++      /* On 32 bit archs roundup_pow_of_two() with max_entries that has
++       * upper most bit set in u32 space is undefined behavior due to
++       * resulting 1U << 32, so do it manually here in u64 space.
++       */
++      mask64 = fls_long(max_entries - 1);
++      mask64 = 1ULL << mask64;
++      mask64 -= 1;
++
++      index_mask = mask64;
++      if (unpriv) {
+               /* round up array size to nearest power of 2,
+                * since cpu will speculate within index_mask limits
+                */
+               max_entries = index_mask + 1;
++              /* Check for overflows. */
++              if (max_entries < attr->max_entries)
++                      return ERR_PTR(-E2BIG);
++      }
+       array_size = sizeof(*array);
+       if (percpu)
diff --git a/queue-4.9/bpf-prevent-out-of-bounds-speculation.patch b/queue-4.9/bpf-prevent-out-of-bounds-speculation.patch
new file mode 100644 (file)
index 0000000..1f577f7
--- /dev/null
@@ -0,0 +1,265 @@
+From b2157399cc9898260d6031c5bfe45fe137c1fbe7 Mon Sep 17 00:00:00 2001
+From: Alexei Starovoitov <ast@kernel.org>
+Date: Sun, 7 Jan 2018 17:33:02 -0800
+Subject: bpf: prevent out-of-bounds speculation
+
+From: Alexei Starovoitov <ast@kernel.org>
+
+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 |    5 ++++-
+ kernel/bpf/arraymap.c        |   31 ++++++++++++++++++++++---------
+ kernel/bpf/verifier.c        |   42 +++++++++++++++++++++++++++++++++++++++---
+ 4 files changed, 67 insertions(+), 13 deletions(-)
+
+--- 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
+--- a/include/linux/bpf_verifier.h
++++ b/include/linux/bpf_verifier.h
+@@ -67,7 +67,10 @@ 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 */
+ };
+--- a/kernel/bpf/arraymap.c
++++ b/kernel/bpf/arraymap.c
+@@ -46,9 +46,10 @@ static int bpf_array_alloc_percpu(struct
+ 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 ||
+@@ -63,11 +64,20 @@ static struct bpf_map *array_map_alloc(u
+       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)
+@@ -77,6 +87,8 @@ static struct bpf_map *array_map_alloc(u
+       array = bpf_map_area_alloc(array_size);
+       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;
+@@ -110,7 +122,7 @@ static void *array_map_lookup_elem(struc
+       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 */
+@@ -122,7 +134,7 @@ static void *percpu_array_map_lookup_ele
+       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)
+@@ -142,7 +154,7 @@ int bpf_percpu_array_copy(struct bpf_map
+        */
+       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;
+@@ -190,10 +202,11 @@ static int array_map_update_elem(struct
+               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;
+ }
+@@ -227,7 +240,7 @@ int bpf_percpu_array_update(struct bpf_m
+        */
+       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;
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -1187,7 +1187,7 @@ static void clear_all_pkt_pointers(struc
+       }
+ }
+-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;
+@@ -1238,6 +1238,13 @@ static int check_call(struct bpf_verifie
+       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;
+@@ -3019,7 +3026,7 @@ static int do_check(struct bpf_verifier_
+                                       return -EINVAL;
+                               }
+-                              err = check_call(env, insn->imm);
++                              err = check_call(env, insn->imm, insn_idx);
+                               if (err)
+                                       return err;
+@@ -3372,7 +3379,11 @@ static int fixup_bpf_calls(struct bpf_ve
+       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))
+@@ -3390,6 +3401,31 @@ static int fixup_bpf_calls(struct bpf_ve
+                        */
+                       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;
+               }
index d80663bd4df753c7be5f9b5bbd3c1960ac93b77f..96008de4e4f23acde2ceae7b98f3f429c3519748 100644 (file)
@@ -50,3 +50,5 @@ iscsi-target-make-task_reassign-use-proper-se_cmd-cmd_kref.patch
 target-avoid-early-cmd_t_pre_execute-failures-during-abort_task.patch
 bpf-move-fixup_bpf_calls-function.patch
 bpf-refactor-fixup_bpf_calls.patch
+bpf-prevent-out-of-bounds-speculation.patch
+bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch