xref: /optee_os/lib/libutils/ext/include/confine_array_index.h (revision 6b40e452c753ea54c32a5a792bf3788637b137bf)
1*6b40e452SJens Wiklander /* SPDX-License-Identifier: BSD-3-Clause */
2*6b40e452SJens Wiklander // Copyright 2019 The Fuchsia Authors. All rights reserved.
3*6b40e452SJens Wiklander // Use of this source code is governed by a BSD-style license that can be
4*6b40e452SJens Wiklander // found in the LICENSE file.
5*6b40e452SJens Wiklander 
6*6b40e452SJens Wiklander /*
7*6b40e452SJens Wiklander  * Content of LICENSE file mentioned above:
8*6b40e452SJens Wiklander Copyright 2019 The Fuchsia Authors. All rights reserved.
9*6b40e452SJens Wiklander Redistribution and use in source and binary forms, with or without
10*6b40e452SJens Wiklander modification, are permitted provided that the following conditions are
11*6b40e452SJens Wiklander met:
12*6b40e452SJens Wiklander    * Redistributions of source code must retain the above copyright
13*6b40e452SJens Wiklander notice, this list of conditions and the following disclaimer.
14*6b40e452SJens Wiklander    * Redistributions in binary form must reproduce the above
15*6b40e452SJens Wiklander copyright notice, this list of conditions and the following disclaimer
16*6b40e452SJens Wiklander in the documentation and/or other materials provided with the
17*6b40e452SJens Wiklander distribution.
18*6b40e452SJens Wiklander    * Neither the name of Google Inc. nor the names of its
19*6b40e452SJens Wiklander contributors may be used to endorse or promote products derived from
20*6b40e452SJens Wiklander this software without specific prior written permission.
21*6b40e452SJens Wiklander THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*6b40e452SJens Wiklander "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*6b40e452SJens Wiklander LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*6b40e452SJens Wiklander A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25*6b40e452SJens Wiklander OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*6b40e452SJens Wiklander SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27*6b40e452SJens Wiklander LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*6b40e452SJens Wiklander DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*6b40e452SJens Wiklander THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*6b40e452SJens Wiklander (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31*6b40e452SJens Wiklander OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*6b40e452SJens Wiklander  */
33*6b40e452SJens Wiklander #ifndef FBL_CONFINE_ARRAY_INDEX_H_
34*6b40e452SJens Wiklander #define FBL_CONFINE_ARRAY_INDEX_H_
35*6b40e452SJens Wiklander 
36*6b40e452SJens Wiklander #include <stddef.h>
37*6b40e452SJens Wiklander 
38*6b40e452SJens Wiklander // confine_array_index() bounds-checks and sanitizes an array index safely in the presence of
39*6b40e452SJens Wiklander // speculative execution information leak bugs such as Spectre V1. confine_array_index() always
40*6b40e452SJens Wiklander // returns a sanitized index, even in speculative-path execution.
41*6b40e452SJens Wiklander //
42*6b40e452SJens Wiklander // Callers need to combine confine_array_index with a conventional bounds check; the bounds
43*6b40e452SJens Wiklander // check will return any necessary errors in the nonspeculative path, confine_array_index will
44*6b40e452SJens Wiklander // confine indexes in the speculative path.
45*6b40e452SJens Wiklander //
46*6b40e452SJens Wiklander // Use:
47*6b40e452SJens Wiklander // confine_array_index() returns |index|, if it is < size, or 0 if |index| is >= size.
48*6b40e452SJens Wiklander //
49*6b40e452SJens Wiklander // Example (may leak table1 contents):
50*6b40e452SJens Wiklander //  1: int lookup3(size_t index) {
51*6b40e452SJens Wiklander //  2:   if (index >= table1_size) {
52*6b40e452SJens Wiklander //  3:     return -1;
53*6b40e452SJens Wiklander //  4:   }
54*6b40e452SJens Wiklander //  5:   size_t index2 = table1[index];
55*6b40e452SJens Wiklander //  6:   return table2[index2];
56*6b40e452SJens Wiklander //  7: }
57*6b40e452SJens Wiklander //
58*6b40e452SJens Wiklander // Converted:
59*6b40e452SJens Wiklander //
60*6b40e452SJens Wiklander //  1: int lookup3(size_t index) {
61*6b40e452SJens Wiklander //  2:   if (index >= table1_size) {
62*6b40e452SJens Wiklander //  3:     return -1;
63*6b40e452SJens Wiklander //  4:   }
64*6b40e452SJens Wiklander //  5:   size_t safe_index = confine_array_index(index, table1_size);
65*6b40e452SJens Wiklander //  6:   size_t index2 = table1[safe_index];
66*6b40e452SJens Wiklander //  7:   return table2[index2];
67*6b40e452SJens Wiklander //  8: }
68*6b40e452SJens Wiklander #ifdef __aarch64__
69*6b40e452SJens Wiklander static inline size_t confine_array_index(size_t index, size_t size) {
70*6b40e452SJens Wiklander   size_t safe_index;
71*6b40e452SJens Wiklander   // Use a conditional select and a CSDB barrier to enforce validation of |index|.
72*6b40e452SJens Wiklander   // See "Cache Speculation Side-channels" whitepaper, section "Software Mitigation".
73*6b40e452SJens Wiklander   // "" The combination of both a conditional select/conditional move and the new barrier are
74*6b40e452SJens Wiklander   // sufficient to address this problem on ALL Arm implementations... ""
75*6b40e452SJens Wiklander   asm(
76*6b40e452SJens Wiklander     "cmp %1, %2\n"  // %1 holds the unsanitized index
77*6b40e452SJens Wiklander     "csel %0, %1, xzr, lo\n"  // Select index or zero based on carry (%1 within range)
78*6b40e452SJens Wiklander     "csdb\n"
79*6b40e452SJens Wiklander   : "=r"(safe_index)
80*6b40e452SJens Wiklander   : "r"(index), "r"(size)
81*6b40e452SJens Wiklander   : "cc");
82*6b40e452SJens Wiklander   return safe_index;
83*6b40e452SJens Wiklander }
84*6b40e452SJens Wiklander #endif
85*6b40e452SJens Wiklander #ifdef __x86_64__
86*6b40e452SJens Wiklander static inline size_t confine_array_index(size_t index, size_t size) {
87*6b40e452SJens Wiklander   size_t safe_index = 0;
88*6b40e452SJens Wiklander   // Use a conditional move to enforce validation of |index|.
89*6b40e452SJens Wiklander   // The conditional move has a data dependency on the result of a comparison and cannot
90*6b40e452SJens Wiklander   // execute until the comparison is resolved.
91*6b40e452SJens Wiklander   // See "Software Techniques for Managing Speculation on AMD Processors", Mitigation V1-2.
92*6b40e452SJens Wiklander   // See "Analyzing potential bounds check bypass vulnerabilities", Revision 002,
93*6b40e452SJens Wiklander   //   Section 5.2 Bounds clipping
94*6b40e452SJens Wiklander   __asm__(
95*6b40e452SJens Wiklander     "cmp %1, %2\n"
96*6b40e452SJens Wiklander     "cmova %1, %0\n"  // Select between $0 and |index|
97*6b40e452SJens Wiklander   : "+r"(safe_index)
98*6b40e452SJens Wiklander   : "r"(index), "r"(size)
99*6b40e452SJens Wiklander   : "cc");
100*6b40e452SJens Wiklander   return safe_index;
101*6b40e452SJens Wiklander }
102*6b40e452SJens Wiklander #endif
103*6b40e452SJens Wiklander #endif  // FBL_CONFINE_ARRAY_INDEX_H_
104