1 #include <stdio.h> 2 #include <unistd.h> 3 #include <assert.h> 4 #include <string.h> 5 #include "freertos/FreeRTOS.h" 6 #include "freertos/task.h" 7 #include "esp_partition.h" 8 #include "esp_flash.h" 9 #include "esp_system.h" 10 11 /* utility functions */ 12 static void die(const char* msg) __attribute__ ((noreturn)); 13 static const char* get_test_name(void); 14 15 /* functions which cause an exception/panic in different ways */ 16 static void test_abort(void); 17 static void test_abort_cache_disabled(void); 18 static void test_int_wdt(void); 19 static void test_task_wdt(void); 20 static void test_storeprohibited(void); 21 static void test_cache_error(void); 22 static void test_int_wdt_cache_disabled(void); 23 static void test_stack_overflow(void); 24 static void test_illegal_instruction(void); 25 static void test_instr_fetch_prohibited(void); 26 static void test_ub(void); 27 static void test_assert(void); 28 static void test_assert_cache_disabled(void); 29 30 app_main(void)31void app_main(void) 32 { 33 /* Needed to allow the tick hook to set correct INT WDT timeouts */ 34 vTaskDelay(2); 35 36 /* Test script sends to command over UART. Read it and determine how to proceed. */ 37 const char* test_name = get_test_name(); 38 if (test_name == NULL) { 39 /* Nothing to do */ 40 return; 41 } 42 printf("Got test name: %s\n", test_name); 43 44 #define HANDLE_TEST(name_) \ 45 if (strcmp(test_name, #name_) == 0) { \ 46 name_(); \ 47 die("Test function has returned"); \ 48 } 49 50 HANDLE_TEST(test_abort); 51 HANDLE_TEST(test_abort_cache_disabled); 52 HANDLE_TEST(test_int_wdt); 53 HANDLE_TEST(test_task_wdt); 54 HANDLE_TEST(test_storeprohibited); 55 HANDLE_TEST(test_cache_error); 56 HANDLE_TEST(test_int_wdt_cache_disabled); 57 HANDLE_TEST(test_stack_overflow); 58 HANDLE_TEST(test_illegal_instruction); 59 HANDLE_TEST(test_instr_fetch_prohibited); 60 HANDLE_TEST(test_ub); 61 HANDLE_TEST(test_assert); 62 HANDLE_TEST(test_assert_cache_disabled); 63 64 #undef HANDLE_TEST 65 66 die("Unknown test name"); 67 } 68 69 /* implementations of the test functions */ 70 test_abort(void)71static void test_abort(void) 72 { 73 abort(); 74 } 75 test_abort_cache_disabled(void)76static void IRAM_ATTR test_abort_cache_disabled(void) 77 { 78 esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); 79 abort(); 80 } 81 test_int_wdt(void)82static void test_int_wdt(void) 83 { 84 portDISABLE_INTERRUPTS(); 85 while (true) { 86 ; 87 } 88 } 89 test_task_wdt(void)90static void test_task_wdt(void) 91 { 92 while (true) { 93 ; 94 } 95 } 96 test_storeprohibited(void)97static void __attribute__((no_sanitize_undefined)) test_storeprohibited(void) 98 { 99 *(int*) 0x1 = 0; 100 } 101 test_cache_error(void)102static IRAM_ATTR void test_cache_error(void) 103 { 104 esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); 105 die("this should not be printed"); 106 } 107 test_int_wdt_cache_disabled(void)108static void IRAM_ATTR test_int_wdt_cache_disabled(void) 109 { 110 esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); 111 portDISABLE_INTERRUPTS(); 112 while (true) { 113 ; 114 } 115 } 116 test_assert(void)117static void test_assert(void) 118 { 119 assert(0); 120 } 121 test_assert_cache_disabled(void)122static void IRAM_ATTR test_assert_cache_disabled(void) 123 { 124 esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data); 125 assert(0); 126 } 127 128 /** 129 * This function overwrites the stack beginning from the valid area continuously towards and beyond 130 * the end of the stack (stack base) of the current task. 131 * This is to test stack protection measures like a watchpoint at the end of the stack. 132 * 133 * @note: This test DOES NOT write beyond the stack limit. It only writes up to exactly the limit itself. 134 * The FreeRTOS stack protection mechanisms all trigger shortly before the end of the stack. 135 */ test_stack_overflow(void)136static void test_stack_overflow(void) 137 { 138 register uint32_t* sp asm("sp"); 139 TaskStatus_t pxTaskStatus; 140 vTaskGetInfo(NULL, &pxTaskStatus, pdFALSE, pdFALSE); 141 uint32_t *end = (uint32_t*) pxTaskStatus.pxStackBase; 142 143 // offset - 20 bytes from SP in order to not corrupt the current frame. 144 // Need to write from higher to lower addresses since the stack grows downwards and the watchpoint/canary is near 145 // the end of the stack (lowest address). 146 for (uint32_t* ptr = sp - 5; ptr != end; --ptr) { 147 *ptr = 0; 148 } 149 150 // trigger a context switch to initiate checking the FreeRTOS stack canary 151 vTaskDelay(pdMS_TO_TICKS(0)); 152 } 153 test_illegal_instruction(void)154static void test_illegal_instruction(void) 155 { 156 #if __XTENSA__ 157 __asm__ __volatile__("ill"); 158 #elif __riscv 159 __asm__ __volatile__("unimp"); 160 #endif 161 } 162 test_instr_fetch_prohibited(void)163static void test_instr_fetch_prohibited(void) 164 { 165 typedef void (*fptr_t)(void); 166 volatile fptr_t fptr = (fptr_t) 0x4; 167 fptr(); 168 } 169 test_ub(void)170static void test_ub(void) 171 { 172 uint8_t stuff[1] = {rand()}; 173 printf("%d\n", stuff[rand()]); 174 } 175 176 /* implementations of the utility functions */ 177 178 #define BOOT_CMD_MAX_LEN (128) 179 get_test_name(void)180static const char* get_test_name(void) 181 { 182 static char test_name_str[BOOT_CMD_MAX_LEN] = {0}; 183 184 printf("Enter test name: "); 185 fflush(stdout); 186 187 /* Not using blocking fgets(stdin) here, as QEMU doesn't yet implement RX timeout interrupt, 188 * which is required for the UART driver and blocking stdio to work. 189 */ 190 int c = EOF; 191 char *p = test_name_str; 192 const char *end = test_name_str + sizeof(test_name_str) - 1; 193 while (p < end) { 194 c = getchar(); 195 if (c == EOF) { 196 vTaskDelay(pdMS_TO_TICKS(10)); 197 } else if (c == '\r') { 198 continue; 199 } else if (c == '\n') { 200 *p = '\0'; 201 break; 202 } else { 203 *p = c; 204 ++p; 205 } 206 } 207 208 return test_name_str; 209 } 210 211 extern void esp_restart_noos(void) __attribute__ ((noreturn)); 212 die(const char * msg)213static void die(const char* msg) 214 { 215 printf("Test error: %s\n\n", msg); 216 fflush(stdout); 217 usleep(1000); 218 /* Don't use abort here as it would enter the panic handler */ 219 esp_restart_noos(); 220 } 221