test_panic_main.c 6.7 KB

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