1 /*
2  * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "sdkconfig.h"
7 #include "soc/rtc_cntl_reg.h"
8 #include "esp_rom_sys.h"
9 
10 #pragma once
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
16 /**
17  * @brief Assert a condition is true, in a way that should be resistant to fault injection for
18  * single fault attacks.
19  *
20  * - Expands CONDITION multiple times (condition must have no side effects)
21  * - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault
22  *   causing a misread of a register used in all three evaluations of CONDITION.
23  * - If CONDITION is ever false, a system reset is triggered.
24  *
25  * @note Place this macro after a "normal" check of CONDITION that will fail with a normal error
26  * message. This is the fallback in case a fault injection attack skips or corrupts the result of
27  * that check. (Although ensure that an attacker can't use fault injection to skip past the "normal"
28  * error message, to avoid this check entirely.)
29  *
30  * @note This macro increases binary size and is slow and should be used sparingly.
31  *
32  * @note This macro does not guarantee fault injection resistance. In particular CONDITION must be
33  * chosen carefully - a fault injection attack which sets CONDITION to true will not be detected by
34  * this macro. Care must also be taken that an attacker can't use a fault to completely bypass calling
35  * whatever function tests ESP_FAULT_ASSERT.
36  *
37  * @note This is difficult to debug as a failure triggers an instant software reset, and UART output
38  * is often truncated (as FIFO is not flushed). Define the ESP_FAULT_ASSERT_DEBUG macro to debug any
39  * failures of this macro due to software bugs.
40  *
41  * @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation.
42  *
43  */
44 #define ESP_FAULT_ASSERT(CONDITION) do {                \
45         asm volatile ("" ::: "memory");                 \
46         if(!(CONDITION)) _ESP_FAULT_RESET();            \
47         asm volatile ("" ::: "memory");                 \
48         if(!(CONDITION)) _ESP_FAULT_RESET();            \
49         asm volatile ("" ::: "memory");                 \
50         if(!(CONDITION)) _ESP_FAULT_RESET();            \
51 } while(0)
52 
53 #ifndef CONFIG_IDF_TARGET_ARCH_RISCV
54 #define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("ill.n; ill.n; ill.n; ill.n; ill.n; ill.n; ill.n;")
55 #else
56 #define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("unimp; unimp; unimp; unimp; unimp;")
57 #endif
58 
59 // Uncomment this macro to get debug output if ESP_FAULT_ASSERT() fails
60 //
61 // Note that uncommenting this macro reduces the anti-FI effectiveness
62 //
63 //#define ESP_FAULT_ASSERT_DEBUG
64 
65 /* Internal macro, purpose is to trigger a system reset if an inconsistency due to fault injection
66    is detected.
67 
68    Illegal instruction opcodes are there as a fallback to crash the CPU in case it doesn't
69    reset as expected.
70 */
71 #ifndef ESP_FAULT_ASSERT_DEBUG
72 
73 #define _ESP_FAULT_RESET()  do {                                \
74         REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);  \
75         _ESP_FAULT_ILLEGAL_INSTRUCTION;                         \
76     } while(0)
77 
78 #else // ESP_FAULT_ASSERT_DEBUG
79 
80 #warning "Enabling ESP_FAULT_ASSERT_DEBUG makes ESP_FAULT_ASSERT() less effective"
81 
82 #define _ESP_FAULT_RESET()  do {                                        \
83         esp_rom_printf("ESP_FAULT_ASSERT %s:%d\n", __FILE__, __LINE__); \
84         _ESP_FAULT_ILLEGAL_INSTRUCTION;                                 \
85     } while(0)
86 
87 #endif // ESP_FAULT_ASSERT_DEBUG
88 
89 #ifdef __cplusplus
90 }
91 #endif
92