esp-idf/tools/test_apps/system/panic/main/test_panic_main.c
Ivan Grokhotkov 7ab57605cb test: panic: make stack overflow test more robust
The previous approach was to allocate an array on the stack, and
have the array extend past the stack size. This worked by would
result in SP being moved near the end of the stack. If an interrupt
triggered at that time, interrupt prologue would try to save the
context to the stack, tripping the stack overflow watchpoint.

Replacing this with the approach which doesn't move the SP and simply
writes to decreasing addresses from SP, until stack overflow check
triggers.
2020-12-30 01:10:09 +01:00

170 lines
4.0 KiB
C

#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_partition.h"
#include "esp_flash.h"
#include "esp_system.h"
/* utility functions */
static void die(const char* msg) __attribute__ ((noreturn));
static const char* get_test_name(void);
/* functions which cause an exception/panic in different ways */
static void test_abort(void);
static void test_int_wdt(void);
static void test_task_wdt(void);
static void test_storeprohibited(void);
static void test_cache_error(void);
static void test_int_wdt_cache_disabled(void);
static void test_stack_overflow(void);
static void test_illegal_instruction(void);
static void test_instr_fetch_prohibited(void);
void app_main(void)
{
/* Needed to allow the tick hook to set correct INT WDT timeouts */
vTaskDelay(2);
/* Test script sends to command over UART. Read it and determine how to proceed. */
const char* test_name = get_test_name();
if (test_name == NULL) {
/* Nothing to do */
return;
}
printf("Got test name: %s\n", test_name);
#define HANDLE_TEST(name_) \
if (strcmp(test_name, #name_) == 0) { \
name_(); \
die("Test function has returned"); \
}
HANDLE_TEST(test_abort);
HANDLE_TEST(test_int_wdt);
HANDLE_TEST(test_task_wdt);
HANDLE_TEST(test_storeprohibited);
HANDLE_TEST(test_cache_error);
HANDLE_TEST(test_int_wdt_cache_disabled);
HANDLE_TEST(test_stack_overflow);
HANDLE_TEST(test_illegal_instruction);
HANDLE_TEST(test_instr_fetch_prohibited);
#undef HANDLE_TEST
die("Unknown test name");
}
/* implementations of the test functions */
static void test_abort(void)
{
abort();
}
static void test_int_wdt(void)
{
portDISABLE_INTERRUPTS();
while (true) {
;
}
}
static void test_task_wdt(void)
{
while (true) {
;
}
}
static void test_storeprohibited(void)
{
*(int*) 0x1 = 0;
}
static IRAM_ATTR void test_cache_error(void)
{
esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data);
die("this should not be printed");
}
static void IRAM_ATTR test_int_wdt_cache_disabled(void)
{
esp_flash_default_chip->os_func->start(esp_flash_default_chip->os_func_data);
portDISABLE_INTERRUPTS();
while (true) {
;
}
}
static void test_stack_overflow(void)
{
register uint32_t* sp asm("sp");
uint32_t *end = sp - CONFIG_ESP_MAIN_TASK_STACK_SIZE;
// offset - 20 bytes from SP in order to not corrupt the current frame.
for (uint32_t* ptr = sp - 5; ptr != end; --ptr) {
*ptr = rand();
}
}
static void test_illegal_instruction(void)
{
__asm__ __volatile__("ill");
}
static void test_instr_fetch_prohibited(void)
{
typedef void (*fptr_t)(void);
volatile fptr_t fptr = (fptr_t) 0x4;
fptr();
}
/* implementations of the utility functions */
#define BOOT_CMD_MAX_LEN (128)
static const char* get_test_name(void)
{
static char test_name_str[BOOT_CMD_MAX_LEN] = {0};
printf("Enter test name: ");
fflush(stdout);
/* Not using blocking fgets(stdin) here, as QEMU doesn't yet implement RX timeout interrupt,
* which is required for the UART driver and blocking stdio to work.
*/
int c = EOF;
char *p = test_name_str;
const char *end = test_name_str + sizeof(test_name_str) - 1;
while (p < end) {
c = getchar();
if (c == EOF) {
vTaskDelay(pdMS_TO_TICKS(10));
} else if (c == '\r') {
continue;
} else if (c == '\n') {
*p = '\0';
break;
} else {
*p = c;
++p;
}
}
return test_name_str;
}
extern void esp_restart_noos(void) __attribute__ ((noreturn));
static void die(const char* msg)
{
printf("Test error: %s\n\n", msg);
fflush(stdout);
fsync(fileno(stdout));
/* Don't use abort here as it would enter the panic handler */
esp_restart_noos();
}