xref: /optee_os/lib/libutils/ext/include/confine_array_index.h (revision 2b6dd0df52b4d44f568892310f90409a595ce0af)
16b40e452SJens Wiklander /* SPDX-License-Identifier: BSD-3-Clause */
2*2b6dd0dfSJens Wiklander /* Copyright (c) 2020 Linaro Limited */
36b40e452SJens Wiklander // Copyright 2019 The Fuchsia Authors. All rights reserved.
46b40e452SJens Wiklander // Use of this source code is governed by a BSD-style license that can be
56b40e452SJens Wiklander // found in the LICENSE file.
66b40e452SJens Wiklander 
76b40e452SJens Wiklander /*
86b40e452SJens Wiklander  * Content of LICENSE file mentioned above:
96b40e452SJens Wiklander Copyright 2019 The Fuchsia Authors. All rights reserved.
106b40e452SJens Wiklander Redistribution and use in source and binary forms, with or without
116b40e452SJens Wiklander modification, are permitted provided that the following conditions are
126b40e452SJens Wiklander met:
136b40e452SJens Wiklander    * Redistributions of source code must retain the above copyright
146b40e452SJens Wiklander notice, this list of conditions and the following disclaimer.
156b40e452SJens Wiklander    * Redistributions in binary form must reproduce the above
166b40e452SJens Wiklander copyright notice, this list of conditions and the following disclaimer
176b40e452SJens Wiklander in the documentation and/or other materials provided with the
186b40e452SJens Wiklander distribution.
196b40e452SJens Wiklander    * Neither the name of Google Inc. nor the names of its
206b40e452SJens Wiklander contributors may be used to endorse or promote products derived from
216b40e452SJens Wiklander this software without specific prior written permission.
226b40e452SJens Wiklander THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
236b40e452SJens Wiklander "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
246b40e452SJens Wiklander LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
256b40e452SJens Wiklander A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
266b40e452SJens Wiklander OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
276b40e452SJens Wiklander SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
286b40e452SJens Wiklander LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
296b40e452SJens Wiklander DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
306b40e452SJens Wiklander THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
316b40e452SJens Wiklander (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
326b40e452SJens Wiklander OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
336b40e452SJens Wiklander  */
346b40e452SJens Wiklander #ifndef FBL_CONFINE_ARRAY_INDEX_H_
356b40e452SJens Wiklander #define FBL_CONFINE_ARRAY_INDEX_H_
366b40e452SJens Wiklander 
376b40e452SJens Wiklander #include <stddef.h>
386b40e452SJens Wiklander 
396b40e452SJens Wiklander // confine_array_index() bounds-checks and sanitizes an array index safely in the presence of
406b40e452SJens Wiklander // speculative execution information leak bugs such as Spectre V1. confine_array_index() always
416b40e452SJens Wiklander // returns a sanitized index, even in speculative-path execution.
426b40e452SJens Wiklander //
436b40e452SJens Wiklander // Callers need to combine confine_array_index with a conventional bounds check; the bounds
446b40e452SJens Wiklander // check will return any necessary errors in the nonspeculative path, confine_array_index will
456b40e452SJens Wiklander // confine indexes in the speculative path.
466b40e452SJens Wiklander //
476b40e452SJens Wiklander // Use:
486b40e452SJens Wiklander // confine_array_index() returns |index|, if it is < size, or 0 if |index| is >= size.
496b40e452SJens Wiklander //
506b40e452SJens Wiklander // Example (may leak table1 contents):
516b40e452SJens Wiklander //  1: int lookup3(size_t index) {
526b40e452SJens Wiklander //  2:   if (index >= table1_size) {
536b40e452SJens Wiklander //  3:     return -1;
546b40e452SJens Wiklander //  4:   }
556b40e452SJens Wiklander //  5:   size_t index2 = table1[index];
566b40e452SJens Wiklander //  6:   return table2[index2];
576b40e452SJens Wiklander //  7: }
586b40e452SJens Wiklander //
596b40e452SJens Wiklander // Converted:
606b40e452SJens Wiklander //
616b40e452SJens Wiklander //  1: int lookup3(size_t index) {
626b40e452SJens Wiklander //  2:   if (index >= table1_size) {
636b40e452SJens Wiklander //  3:     return -1;
646b40e452SJens Wiklander //  4:   }
656b40e452SJens Wiklander //  5:   size_t safe_index = confine_array_index(index, table1_size);
666b40e452SJens Wiklander //  6:   size_t index2 = table1[safe_index];
676b40e452SJens Wiklander //  7:   return table2[index2];
686b40e452SJens Wiklander //  8: }
696b40e452SJens Wiklander #ifdef __aarch64__
706b40e452SJens Wiklander static inline size_t confine_array_index(size_t index, size_t size) {
716b40e452SJens Wiklander   size_t safe_index;
726b40e452SJens Wiklander   // Use a conditional select and a CSDB barrier to enforce validation of |index|.
736b40e452SJens Wiklander   // See "Cache Speculation Side-channels" whitepaper, section "Software Mitigation".
746b40e452SJens Wiklander   // "" The combination of both a conditional select/conditional move and the new barrier are
756b40e452SJens Wiklander   // sufficient to address this problem on ALL Arm implementations... ""
766b40e452SJens Wiklander   asm(
776b40e452SJens Wiklander     "cmp %1, %2\n"  // %1 holds the unsanitized index
786b40e452SJens Wiklander     "csel %0, %1, xzr, lo\n"  // Select index or zero based on carry (%1 within range)
796b40e452SJens Wiklander     "csdb\n"
806b40e452SJens Wiklander   : "=r"(safe_index)
816b40e452SJens Wiklander   : "r"(index), "r"(size)
826b40e452SJens Wiklander   : "cc");
836b40e452SJens Wiklander   return safe_index;
846b40e452SJens Wiklander }
856b40e452SJens Wiklander #endif
86*2b6dd0dfSJens Wiklander #ifdef __arm__
87*2b6dd0dfSJens Wiklander static inline size_t confine_array_index(size_t index, size_t size)
88*2b6dd0dfSJens Wiklander {
89*2b6dd0dfSJens Wiklander 	size_t safe_index = 0;
90*2b6dd0dfSJens Wiklander 
91*2b6dd0dfSJens Wiklander 	/*
92*2b6dd0dfSJens Wiklander 	 * For the ARMv7/AArch32 case we're basing the select and barrier
93*2b6dd0dfSJens Wiklander 	 * code on __load_no_speculate1() in <speculation_barrier.h> as we
94*2b6dd0dfSJens Wiklander 	 * lack the csel instruction.
95*2b6dd0dfSJens Wiklander 	 */
96*2b6dd0dfSJens Wiklander 
97*2b6dd0dfSJens Wiklander #ifdef __thumb2__
98*2b6dd0dfSJens Wiklander       asm volatile (
99*2b6dd0dfSJens Wiklander 	".syntax unified\n"
100*2b6dd0dfSJens Wiklander 	"cmp	%1, %2\n" /* %1 holds the unsanitized index */
101*2b6dd0dfSJens Wiklander 	"it	cs\n"
102*2b6dd0dfSJens Wiklander 	"movcs	%1, #0\n"
103*2b6dd0dfSJens Wiklander 	".inst.n 0xf3af\t@ CSDB\n"
104*2b6dd0dfSJens Wiklander 	".inst.n 0x8014\t@ CSDB"
105*2b6dd0dfSJens Wiklander 	: "=r" (safe_index) : "r" (index), "r" (size) : "cc");
106*2b6dd0dfSJens Wiklander #else
107*2b6dd0dfSJens Wiklander       asm volatile (
108*2b6dd0dfSJens Wiklander 	".syntax unified\n"
109*2b6dd0dfSJens Wiklander 	"cmp	%1, %2\n" /* %1 holds the unsanitized index */
110*2b6dd0dfSJens Wiklander 	"movcs	%1, #0\n"
111*2b6dd0dfSJens Wiklander 	".inst	0xe320f014\t@ CSDB"
112*2b6dd0dfSJens Wiklander 	: "=r" (safe_index) : "r" (index), "r" (size) : "cc");
113*2b6dd0dfSJens Wiklander #endif
114*2b6dd0dfSJens Wiklander 
115*2b6dd0dfSJens Wiklander 	return safe_index;
116*2b6dd0dfSJens Wiklander }
117*2b6dd0dfSJens Wiklander #endif /* __arm__ */
118*2b6dd0dfSJens Wiklander 
1196b40e452SJens Wiklander #ifdef __x86_64__
1206b40e452SJens Wiklander static inline size_t confine_array_index(size_t index, size_t size) {
1216b40e452SJens Wiklander   size_t safe_index = 0;
1226b40e452SJens Wiklander   // Use a conditional move to enforce validation of |index|.
1236b40e452SJens Wiklander   // The conditional move has a data dependency on the result of a comparison and cannot
1246b40e452SJens Wiklander   // execute until the comparison is resolved.
1256b40e452SJens Wiklander   // See "Software Techniques for Managing Speculation on AMD Processors", Mitigation V1-2.
1266b40e452SJens Wiklander   // See "Analyzing potential bounds check bypass vulnerabilities", Revision 002,
1276b40e452SJens Wiklander   //   Section 5.2 Bounds clipping
1286b40e452SJens Wiklander   __asm__(
1296b40e452SJens Wiklander     "cmp %1, %2\n"
1306b40e452SJens Wiklander     "cmova %1, %0\n"  // Select between $0 and |index|
1316b40e452SJens Wiklander   : "+r"(safe_index)
1326b40e452SJens Wiklander   : "r"(index), "r"(size)
1336b40e452SJens Wiklander   : "cc");
1346b40e452SJens Wiklander   return safe_index;
1356b40e452SJens Wiklander }
1366b40e452SJens Wiklander #endif
1376b40e452SJens Wiklander #endif  // FBL_CONFINE_ARRAY_INDEX_H_
138