Back to home page

OSCL-LXR

 
 

    


0001 ===========
0002 Speculation
0003 ===========
0004 
0005 This document explains potential effects of speculation, and how undesirable
0006 effects can be mitigated portably using common APIs.
0007 
0008 ------------------------------------------------------------------------------
0009 
0010 To improve performance and minimize average latencies, many contemporary CPUs
0011 employ speculative execution techniques such as branch prediction, performing
0012 work which may be discarded at a later stage.
0013 
0014 Typically speculative execution cannot be observed from architectural state,
0015 such as the contents of registers. However, in some cases it is possible to
0016 observe its impact on microarchitectural state, such as the presence or
0017 absence of data in caches. Such state may form side-channels which can be
0018 observed to extract secret information.
0019 
0020 For example, in the presence of branch prediction, it is possible for bounds
0021 checks to be ignored by code which is speculatively executed. Consider the
0022 following code::
0023 
0024         int load_array(int *array, unsigned int index)
0025         {
0026                 if (index >= MAX_ARRAY_ELEMS)
0027                         return 0;
0028                 else
0029                         return array[index];
0030         }
0031 
0032 Which, on arm64, may be compiled to an assembly sequence such as::
0033 
0034         CMP     <index>, #MAX_ARRAY_ELEMS
0035         B.LT    less
0036         MOV     <returnval>, #0
0037         RET
0038   less:
0039         LDR     <returnval>, [<array>, <index>]
0040         RET
0041 
0042 It is possible that a CPU mis-predicts the conditional branch, and
0043 speculatively loads array[index], even if index >= MAX_ARRAY_ELEMS. This
0044 value will subsequently be discarded, but the speculated load may affect
0045 microarchitectural state which can be subsequently measured.
0046 
0047 More complex sequences involving multiple dependent memory accesses may
0048 result in sensitive information being leaked. Consider the following
0049 code, building on the prior example::
0050 
0051         int load_dependent_arrays(int *arr1, int *arr2, int index)
0052         {
0053                 int val1, val2,
0054 
0055                 val1 = load_array(arr1, index);
0056                 val2 = load_array(arr2, val1);
0057 
0058                 return val2;
0059         }
0060 
0061 Under speculation, the first call to load_array() may return the value
0062 of an out-of-bounds address, while the second call will influence
0063 microarchitectural state dependent on this value. This may provide an
0064 arbitrary read primitive.
0065 
0066 ====================================
0067 Mitigating speculation side-channels
0068 ====================================
0069 
0070 The kernel provides a generic API to ensure that bounds checks are
0071 respected even under speculation. Architectures which are affected by
0072 speculation-based side-channels are expected to implement these
0073 primitives.
0074 
0075 The array_index_nospec() helper in <linux/nospec.h> can be used to
0076 prevent information from being leaked via side-channels.
0077 
0078 A call to array_index_nospec(index, size) returns a sanitized index
0079 value that is bounded to [0, size) even under cpu speculation
0080 conditions.
0081 
0082 This can be used to protect the earlier load_array() example::
0083 
0084         int load_array(int *array, unsigned int index)
0085         {
0086                 if (index >= MAX_ARRAY_ELEMS)
0087                         return 0;
0088                 else {
0089                         index = array_index_nospec(index, MAX_ARRAY_ELEMS);
0090                         return array[index];
0091                 }
0092         }