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