From: Mark Rutland <mark.rutland@arm.com>
To: linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org
Cc: dan.j.williams@intel.com, elena.reshetova@intel.com,
corbet@lwn.net, alan@linux.intel.com, peterz@infradead.org,
will.deacon@arm.com, gregkh@linuxfoundation.org,
tglx@linutronix.de, Mark Rutland <mark.rutland@arm.com>
Subject: [RFCv2 2/4] Documentation: document nospec helpers
Date: Fri, 5 Jan 2018 14:57:48 +0000 [thread overview]
Message-ID: <20180105145750.53294-3-mark.rutland@arm.com> (raw)
In-Reply-To: <20180105145750.53294-1-mark.rutland@arm.com>
Document the rationale and usage of the new nospec*() helpers.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Peter Zijlstra <peterz@infradead.org>
---
Documentation/speculation.txt | 166 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 Documentation/speculation.txt
diff --git a/Documentation/speculation.txt b/Documentation/speculation.txt
new file mode 100644
index 000000000000..748fcd4dcda4
--- /dev/null
+++ b/Documentation/speculation.txt
@@ -0,0 +1,166 @@
+This document explains potential effects of speculation, and how undesirable
+effects can be mitigated portably using common APIs.
+
+===========
+Speculation
+===========
+
+To improve performance and minimize average latencies, many contemporary CPUs
+employ speculative execution techniques such as branch prediction, performing
+work which may be discarded at a later stage.
+
+Typically speculative execution cannot be observed from architectural state,
+such as the contents of registers. However, in some cases it is possible to
+observe its impact on microarchitectural state, such as the presence or
+absence of data in caches. Such state may form side-channels which can be
+observed to extract secret information.
+
+For example, in the presence of branch prediction, it is possible for bounds
+checks to be ignored by code which is speculatively executed. Consider the
+following code:
+
+ int load_array(int *array, unsigned int idx) {
+ if (idx >= MAX_ARRAY_ELEMS)
+ return 0;
+ else
+ return array[idx];
+ }
+
+Which, on arm64, may be compiled to an assembly sequence such as:
+
+ CMP <idx>, #MAX_ARRAY_ELEMS
+ B.LT less
+ MOV <returnval>, #0
+ RET
+ less:
+ LDR <returnval>, [<array>, <idx>]
+ RET
+
+It is possible that a CPU mis-predicts the conditional branch, and
+speculatively loads array[idx], even if idx >= MAX_ARRAY_ELEMS. This value
+will subsequently be discarded, but the speculated load may affect
+microarchitectural state which can be subsequently measured.
+
+More complex sequences involving multiple dependent memory accesses may result
+in sensitive information being leaked. Consider the following code, building on
+the prior example:
+
+ int load_dependent_arrays(int *arr1, int *arr2, int idx) {
+ int val1, val2,
+
+ val1 = load_array(arr1, idx);
+ val2 = load_array(arr2, val1);
+
+ return val2;
+ }
+
+Under speculation, the first call to load_array() may return the value of an
+out-of-bounds address, while the second call will influence microarchitectural
+state dependent on this value. This may provide an arbitrary read primitive.
+
+====================================
+Mitigating speculation side-channels
+====================================
+
+The kernel provides a generic API to ensure that bounds checks are respected
+even under speculation. Architectures which are affected by speculation-based
+side-channels are expected to implement these primitives.
+
+The following helpers found in <asm/barrier.h> can be used to prevent
+information from being leaked via side-channels.
+
+* nospec_ptr(ptr, lo, hi)
+
+ Returns a sanitized pointer that is bounded by the [lo, hi) interval. When
+ ptr < lo, or ptr >= hi, NULL is returned. Prevents an out-of-bounds pointer
+ being propagated to code which is speculatively executed.
+
+ This is expected to be used by code which computes pointers to data
+ structures, where part of the address (such as an array index) may be
+ user-controlled.
+
+ This can be used to protect the earlier load_array() example:
+
+ int load_array(int *array, unsigned int idx)
+ {
+ int *elem;
+
+ if ((elem = nospec_ptr(array + idx, array, array + MAX_ARRAY_ELEMS)))
+ return *elem;
+ else
+ return 0;
+ }
+
+ This can also be used in situations where multiple fields on a structure are
+ accessed:
+
+ struct foo array[SIZE];
+ int a, b;
+
+ void do_thing(int idx)
+ {
+ struct foo *elem;
+
+ if ((elem = nospec_ptr(array + idx, array, array + SIZE)) {
+ a = elem->field_a;
+ b = elem->field_b;
+ }
+ }
+
+ It is imperative that the returned pointer is used. Pointers which are
+ generated separately are subject to a number of potential CPU and compiler
+ optimizations, and may still be used speculatively. For example, this means
+ that the following sequence is unsafe:
+
+ struct foo array[SIZE];
+ int a, b;
+
+ void do_thing(int idx)
+ {
+ if (nospec_ptr(array + idx, array, array + SIZE) != NULL) {
+ // unsafe as wrong pointer is used
+ a = array[idx].field_a;
+ b = array[idx].field_b;
+ }
+ }
+
+ Similarly, it is unsafe to compare the returned pointer with other pointers,
+ as this may permit the compiler to substitute one pointer with another,
+ permitting speculation. For example, the following sequence is unsafe:
+
+ struct foo array[SIZE];
+ int a, b;
+
+ void do_thing(int idx)
+ {
+ struct foo *elem = nospec_ptr(array + idx, array, array + size);
+
+ // unsafe due to pointer substitution
+ if (elem == &array[idx]) {
+ a = elem->field_a;
+ b = elem->field_b;
+ }
+ }
+
+* nospec_array_ptr(arr, idx, sz)
+
+ Returns a sanitized pointer to arr[idx] only if idx falls in the [0, sz)
+ interval. When idx < 0 or idx > sz, NULL is returned. Prevents an
+ out-of-bounds pointer being propagated to code which is speculatively
+ executed.
+
+ This is a convenience function which wraps nospec_ptr(), and has the same
+ caveats w.r.t. the use of the returned pointer.
+
+ For example, this may be used as follows:
+
+ int load_array(int *array, unsigned int idx)
+ {
+ int *elem;
+
+ if ((elem = nospec_array_ptr(array, idx, MAX_ARRAY_ELEMS)))
+ return *elem;
+ else
+ return 0;
+ }
+
--
2.11.0
next prev parent reply other threads:[~2018-01-05 14:57 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-01-05 14:57 [RFCv2 0/4] API for inhibiting speculative arbitrary read primitives Mark Rutland
2018-01-05 14:57 ` Mark Rutland
2018-01-05 14:57 ` [RFCv2 1/4] asm-generic/barrier: add generic nospec helpers Mark Rutland
2018-01-05 14:57 ` Mark Rutland
2018-01-08 21:47 ` Mark Salter
2018-01-05 14:57 ` Mark Rutland [this message]
2018-01-05 14:57 ` [RFCv2 2/4] Documentation: document " Mark Rutland
2018-01-07 5:20 ` Randy Dunlap
2018-01-07 13:06 ` Mark Rutland
2018-01-07 10:27 ` Geert Uytterhoeven
2018-01-07 10:27 ` Geert Uytterhoeven
2018-01-05 14:57 ` [RFCv2 3/4] arm64: implement nospec_ptr() Mark Rutland
2018-01-05 14:57 ` [RFCv2 4/4] bpf: inhibit speculated out-of-bounds pointers Mark Rutland
2018-01-05 14:57 ` Mark Rutland
2018-01-05 16:38 ` Dan Williams
2018-01-05 16:48 ` Mark Rutland
2018-01-05 17:04 ` Alexei Starovoitov
2018-01-08 12:59 ` Reshetova, Elena
2018-01-08 12:59 ` Reshetova, Elena
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20180105145750.53294-3-mark.rutland@arm.com \
--to=mark.rutland@arm.com \
--cc=alan@linux.intel.com \
--cc=corbet@lwn.net \
--cc=dan.j.williams@intel.com \
--cc=elena.reshetova@intel.com \
--cc=gregkh@linuxfoundation.org \
--cc=linux-arch@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=peterz@infradead.org \
--cc=tglx@linutronix.de \
--cc=will.deacon@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).