16b40e452SJens Wiklander /* SPDX-License-Identifier: BSD-3-Clause */
22b6dd0dfSJens 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__
confine_array_index(size_t index,size_t size)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)
79bef33113SJerome Forissier "hint #20\n" // csdb
806b40e452SJens Wiklander : "=r"(safe_index)
816b40e452SJens Wiklander : "r"(index), "r"(size)
826b40e452SJens Wiklander : "cc");
836b40e452SJens Wiklander return safe_index;
846b40e452SJens Wiklander }
856b40e452SJens Wiklander #endif
862b6dd0dfSJens Wiklander #ifdef __arm__
confine_array_index(size_t index,size_t size)872b6dd0dfSJens Wiklander static inline size_t confine_array_index(size_t index, size_t size)
882b6dd0dfSJens Wiklander {
89c7c07720SEtienne Carriere size_t ret_val = index;
902b6dd0dfSJens Wiklander
912b6dd0dfSJens Wiklander /*
922b6dd0dfSJens Wiklander * For the ARMv7/AArch32 case we're basing the select and barrier
932b6dd0dfSJens Wiklander * code on __load_no_speculate1() in <speculation_barrier.h> as we
942b6dd0dfSJens Wiklander * lack the csel instruction.
952b6dd0dfSJens Wiklander */
962b6dd0dfSJens Wiklander
972b6dd0dfSJens Wiklander #ifdef __thumb2__
982b6dd0dfSJens Wiklander asm volatile (
992b6dd0dfSJens Wiklander ".syntax unified\n"
100c7c07720SEtienne Carriere "cmp %0, %1\n"
1012b6dd0dfSJens Wiklander "it cs\n"
1027540cb75SEtienne Carriere #ifdef __clang__
1037540cb75SEtienne Carriere #pragma clang diagnostic push
1047540cb75SEtienne Carriere /* Avoid 'deprecated instruction in IT block [-Werror,-Winline-asm]' */
1057540cb75SEtienne Carriere #pragma clang diagnostic ignored "-Winline-asm"
1067540cb75SEtienne Carriere #endif
107c7c07720SEtienne Carriere "movcs %0, #0\n"
1087540cb75SEtienne Carriere #ifdef __clang__
1097540cb75SEtienne Carriere #pragma clang diagnostic pop
1107540cb75SEtienne Carriere #endif
1112b6dd0dfSJens Wiklander ".inst.n 0xf3af\t@ CSDB\n"
1122b6dd0dfSJens Wiklander ".inst.n 0x8014\t@ CSDB"
113c7c07720SEtienne Carriere : "+r" (ret_val) : "r" (size) : "cc");
1142b6dd0dfSJens Wiklander #else
1152b6dd0dfSJens Wiklander asm volatile (
1162b6dd0dfSJens Wiklander ".syntax unified\n"
117c7c07720SEtienne Carriere "cmp %0, %1\n" /* %0 holds the unsanitized index */
118c7c07720SEtienne Carriere "movcs %0, #0\n"
1192b6dd0dfSJens Wiklander ".inst 0xe320f014\t@ CSDB"
120c7c07720SEtienne Carriere : "+r" (ret_val) : "r" (size) : "cc");
1212b6dd0dfSJens Wiklander #endif
1222b6dd0dfSJens Wiklander
123c7c07720SEtienne Carriere return ret_val;
1242b6dd0dfSJens Wiklander }
1252b6dd0dfSJens Wiklander #endif /* __arm__ */
1262b6dd0dfSJens Wiklander
1276b40e452SJens Wiklander #ifdef __x86_64__
confine_array_index(size_t index,size_t size)1286b40e452SJens Wiklander static inline size_t confine_array_index(size_t index, size_t size) {
1296b40e452SJens Wiklander size_t safe_index = 0;
1306b40e452SJens Wiklander // Use a conditional move to enforce validation of |index|.
1316b40e452SJens Wiklander // The conditional move has a data dependency on the result of a comparison and cannot
1326b40e452SJens Wiklander // execute until the comparison is resolved.
1336b40e452SJens Wiklander // See "Software Techniques for Managing Speculation on AMD Processors", Mitigation V1-2.
1346b40e452SJens Wiklander // See "Analyzing potential bounds check bypass vulnerabilities", Revision 002,
1356b40e452SJens Wiklander // Section 5.2 Bounds clipping
1366b40e452SJens Wiklander __asm__(
1376b40e452SJens Wiklander "cmp %1, %2\n"
1386b40e452SJens Wiklander "cmova %1, %0\n" // Select between $0 and |index|
1396b40e452SJens Wiklander : "+r"(safe_index)
1406b40e452SJens Wiklander : "r"(index), "r"(size)
1416b40e452SJens Wiklander : "cc");
1426b40e452SJens Wiklander return safe_index;
1436b40e452SJens Wiklander }
1446b40e452SJens Wiklander #endif
145*f197f055Sliushiwei
146*f197f055Sliushiwei #ifdef __riscv
confine_array_index(size_t index,size_t size)147*f197f055Sliushiwei static inline size_t confine_array_index(size_t index, size_t size) {
148*f197f055Sliushiwei /*
149*f197f055Sliushiwei * The naive C implementation without protection
150*f197f055Sliushiwei * against side-channel attacks.
151*f197f055Sliushiwei */
152*f197f055Sliushiwei if (index < size)
153*f197f055Sliushiwei return index;
154*f197f055Sliushiwei return 0;
155*f197f055Sliushiwei }
156*f197f055Sliushiwei #endif
1576b40e452SJens Wiklander #endif // FBL_CONFINE_ARRAY_INDEX_H_
158