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