Patch "bpf: prevent out-of-bounds speculation" has been added to the 4.4-stable tree
Subsystems (3)
This is a note to let you know that I've just added the patch titled
bpf: prevent out-of-bounds speculation
to the 4.4-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
bpf-prevent-out-of-bounds-speculation.patch
and it can be found in the queue-4.4 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
b2157399cc9898260d6031c5bfe45fe137c1fbe7Mon Sep 17 00:00:00 2001
From: Alexei Starovoitov <[email protected]>
Date: Sun, 7 Jan 2018 17:33:02 -0800
Subject: bpf: prevent out-of-bounds speculation
From: Alexei Starovoitov <[email protected]>
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.
--- include/linux/bpf.h | 2 ++ kernel/bpf/arraymap.c | 24 +++++++++++++++++++----- kernel/bpf/verifier.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 9 deletions(-)
include/linux/bpf.h +2 -0
@@ -37,6 +37,7 @@ struct bpf_map {u32 value_size;u32 max_entries;u32 pages; +bool unpriv_array;struct user_struct *user;const struct bpf_map_ops *ops;struct work_struct work;
@@ -141,6 +142,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
kernel/bpf/arraymap.c +19 -5
@@ -20,8 +20,9 @@/* Called from syscall */ static struct bpf_map *array_map_alloc (union bpf_attr *attr) { +u32 elem_size, array_size, index_mask, max_entries; +bool unpriv = !capable (CAP_SYS_ADMIN );struct bpf_array *array; -u32 elem_size, array_size;/* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 ||
@@ -36,12 +37,21 @@ 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 ; +/* check round_up into zero and u32 overflow */ if (elem_size ==0 || - attr->max_entries > (U32_MAX -PAGE_SIZE -sizeof (*array)) / elem_size) + max_entries > (U32_MAX -PAGE_SIZE -sizeof (*array)) / elem_size)return ERR_PTR (-ENOMEM ); - array_size =sizeof (*array) + attr->max_entries * elem_size; + array_size =sizeof (*array) + max_entries * elem_size;/* allocate all map elements and zero-initialize them */ array =kzalloc (array_size,GFP_USER | __GFP_NOWARN);
@@ -50,6 +60,8 @@ static struct bpf_map *array_map_alloc(uif (!array)return ERR_PTR (-ENOMEM ); } + array->index_mask = index_mask; + array->map.unpriv_array = unpriv;/* copy mandatory map attributes */ array->map.key_size = attr->key_size;
@@ -70,7 +82,7 @@ static void *array_map_lookup_elem(strucif (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 syscall */
@@ -111,7 +123,9 @@ static int array_map_update_elem(struct/* all elements already exist */ return -EEXIST ; -memcpy (array->value + array->elem_size * index, value, map->value_size); +memcpy (array->value + + array->elem_size * (index & array->index_mask), + value, map->value_size);return 0 ; }
kernel/bpf/verifier.c +42 -4
82 lines not on this page · raw message
Patches currently in stable-queue which might be from [email protected] are
queue-4.4/bpf-adjust-insn_aux_data-when-patching-insns.patch queue-4.4/bpf-refactor-fixup_bpf_calls.patch queue-4.4/bpf-array-fix-overflow-in-max_entries-and-undefined-behavior-in-index_mask.patch queue-4.4/bpf-don-t-ab-use-instructions-to-store-state.patch queue-4.4/bpf-prevent-out-of-bounds-speculation.patch queue-4.4/bpf-add-bpf_patch_insn_single-helper.patch queue-4.4/bpf-move-fixup_bpf_calls-function.patch