This commit is contained in:
Chip Weinberger 2024-07-05 14:24:08 +00:00 committed by GitHub
commit f8ada804bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 243 additions and 4 deletions

View File

@ -28,6 +28,10 @@ if(NOT BOOTLOADER_BUILD)
list(APPEND compile_options "-O2")
endif()
if(CONFIG_COMPILER_STACK_MIRROR)
list(APPEND compile_options "-finstrument-functions")
endif()
else() # BOOTLOADER_BUILD
if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE)

16
Kconfig
View File

@ -384,6 +384,22 @@ mainmenu "Espressif IoT Development Framework Configuration"
help
Stack smashing protection.
config COMPILER_STACK_MIRROR
prompt "Enable Stack Mirror"
bool
default "n"
help
Make corrupted backtraces much less likely by creating a dupliate copy of stack return
addresses in heap memory. Requires about 100 bytes of memory per thread.
config COMPILER_STACK_MIRROR_DEPTH
prompt "Stack Mirror Depth"
int
default 26
depends on COMPILER_STACK_MIRROR
help
The maximum depth of the stack mirror. 4 bytes are needed per depth.
config COMPILER_WARN_WRITE_STRINGS
bool "Enable -Wwrite-strings warning flag"
default "n"

View File

@ -64,6 +64,7 @@ static inline void __attribute__((always_inline)) spinlock_initialize(spinlock_t
* @param lock - target spinlock object
* @param timeout - cycles to wait, passing SPINLOCK_WAIT_FOREVER blocs indefinitely
*/
static inline bool __attribute__((always_inline)) spinlock_acquire(spinlock_t *lock, int32_t timeout)
{
#if !CONFIG_FREERTOS_UNICORE && !BOOTLOADER_BUILD

View File

@ -20,6 +20,7 @@ else()
"startup.c"
"system_time.c"
"stack_check.c"
"stack_mirror.c"
"task_wdt.c"
"ubsan.c"
"xt_wdt.c"

View File

@ -0,0 +1,10 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#if CONFIG_COMPILER_STACK_MIRROR
void stack_mirror_enable();
void stack_mirror_print_backtrace(bool panic);
#endif

View File

@ -22,6 +22,7 @@
#include "soc/soc_memory_layout.h"
#include "soc/cpu.h"
#include "esp_private/panic_internal.h"
#include "esp_private/stack_mirror.h"
#include "xtensa/xtensa_context.h"
@ -94,6 +95,9 @@ esp_err_t IRAM_ATTR esp_backtrace_print_from_frame(int depth, const esp_backtrac
esp_err_t ret = ESP_OK;
if (corrupted) {
print_str(" |<-CORRUPTED", panic);
#ifndef CONFIG_COMPILER_STACK_MIRROR
print_str("\r\nTurn on Stack Mirroring to avoid corruption", panic);
#endif
ret = ESP_FAIL;
} else if (stk_frame.next_pc != 0) { //Backtrace continues
print_str(" |<-CONTINUES", panic);
@ -107,5 +111,12 @@ esp_err_t IRAM_ATTR esp_backtrace_print(int depth)
//Initialize stk_frame with first frame of stack
esp_backtrace_frame_t start = { 0 };
esp_backtrace_get_start(&(start.pc), &(start.sp), &(start.next_pc));
return esp_backtrace_print_from_frame(depth, &start, false);
esp_err_t err = esp_backtrace_print_from_frame(depth, &start, false);
#if CONFIG_COMPILER_STACK_MIRROR
if (err != ESP_OK) {
stack_mirror_print_backtrace(false);
}
#endif
return err;
}

View File

@ -11,6 +11,7 @@
#include "esp_debug_helpers.h"
#include "esp_private/panic_internal.h"
#include "esp_private/stack_mirror.h"
#include "esp_private/panic_reason.h"
#include "soc/soc.h"
@ -467,5 +468,9 @@ void panic_print_backtrace(const void *f, int core)
{
XtExcFrame *xt_frame = (XtExcFrame *) f;
esp_backtrace_frame_t frame = {.pc = xt_frame->pc, .sp = xt_frame->a1, .next_pc = xt_frame->a0, .exc_frame = xt_frame};
esp_backtrace_print_from_frame(100, &frame, true);
if (esp_backtrace_print_from_frame(100, &frame, true) != ESP_OK) {
#if CONFIG_COMPILER_STACK_MIRROR
stack_mirror_print_backtrace(true);
#endif
}
}

View File

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_caps.h"
#include "esp_private/panic_internal.h"
#include "soc/soc_memory_types.h"
#if CONFIG_COMPILER_STACK_MIRROR
#define STACK_MIRROR_MAGIC 139871234
/* __cyg_profile_func_enter - gcc inserts a call to this function at the start of every function
* __cyg_profile_func_exit - gcc inserts a call to this function at the end of every function
* no_instrument_function - This tells GCC to not insert instrumentation calls (would cause an infinite loop) */
void IRAM_ATTR __cyg_profile_func_enter(void *func, void *caller) __attribute__((no_instrument_function));
void IRAM_ATTR __cyg_profile_func_exit(void *func, void *caller) __attribute__((no_instrument_function));
static void IRAM_ATTR print_str(const char* str, bool panic) __attribute__((no_instrument_function));
static void IRAM_ATTR print_entry(uint32_t pc, bool panic) __attribute__((no_instrument_function));
static uint32_t enabled;
static void IRAM_ATTR print_str(const char* str, bool panic)
{
if (panic) {
panic_print_str(str);
} else {
esp_rom_printf(str);
}
}
static void IRAM_ATTR print_entry(uint32_t pc, bool panic)
{
if (panic) {
panic_print_str(" 0x");
panic_print_hex(pc);
} else {
esp_rom_printf(" 0x%08X", pc);
}
}
extern void xt_unhandled_exception(XtExcFrame *frame);
void IRAM_ATTR __cyg_profile_func_enter(void *func, void *callsite)
{
if (enabled == STACK_MIRROR_MAGIC) {
esp_stack_mirror_t *m = pvTaskGetStackMirrorPointer();
if (func == xt_unhandled_exception) {
// dont add to the stack trace after we already hit panic.
m->panicing = true;
}
if (!m->panicing) {
if (m->depth < CONFIG_COMPILER_STACK_MIRROR_DEPTH) {
m->backtrace[m->depth] = (uint32_t) func;
}
m->depth++;
}
}
}
void IRAM_ATTR __cyg_profile_func_exit(void *func, void *callsite)
{
if (enabled == STACK_MIRROR_MAGIC) {
esp_stack_mirror_t *m = pvTaskGetStackMirrorPointer();
if (!m->panicing && m->depth > 0) {
m->depth--;
}
}
}
void IRAM_ATTR stack_mirror_print_backtrace(bool panic)
{
esp_stack_mirror_t *m = pvTaskGetStackMirrorPointer();
print_str("\r\nBacktrace (Mirror):", panic);
if (m == NULL) {
print_str("\r\nNot Set", panic);
} else if (esp_ptr_internal(m) == false) {
print_str("\r\nInvalid Ptr", panic);
} else if (m->depth == 0) {
print_str("\r\nEmpty", panic);
} else {
uint32_t depth = m->depth;
if (depth >= CONFIG_COMPILER_STACK_MIRROR_DEPTH) {
print_str("\r\nBACKTRACE INCOMPLETE", panic);
depth = CONFIG_COMPILER_STACK_MIRROR_DEPTH;
}
for (int i = depth - 1; i >= 0; i--) {
uint32_t pc = m->backtrace[i];
print_entry(pc, panic);
bool corrupted = !esp_ptr_executable((void *)esp_cpu_process_stack_pc(pc));
if (corrupted) {
print_str(" |<-CORRUPTED", panic);
break;
}
}
}
print_str("\r\n\r\n", panic);
return;
}
void stack_mirror_enable()
{
enabled = STACK_MIRROR_MAGIC;
}
#endif // CONFIG_COMPILER_STACK_MIRROR

View File

@ -173,8 +173,10 @@ menu "FreeRTOS"
FreeRTOS has the ability to store per-thread pointers in the task
control block. This controls the number of pointers available.
This value must be at least 1. Index 0 is reserved for use by the pthreads API
thread-local-storage. Other indexes can be used for any desired purpose.
This value must be at least 1. Index 0 is reserved for use by the pthreads API thread-local-storage.
Other indexes can be used for any desired purpose.
choice FREERTOS_ASSERT
prompt "FreeRTOS assertions"

View File

@ -1254,6 +1254,12 @@ typedef struct xSTATIC_TCB
#if ( configUSE_POSIX_ERRNO == 1 )
int iDummy22;
#endif
#if CONFIG_COMPILER_STACK_MIRROR
uint32_t uDummy23;
uint32_t uDummy24;
uint32_t uDummy25[CONFIG_COMPILER_STACK_MIRROR_DEPTH];
#endif
} StaticTask_t;
/*

View File

@ -1818,6 +1818,16 @@ configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVIL
*/
uint8_t* pxTaskGetStackStart( TaskHandle_t xTask) PRIVILEGED_FUNCTION;
#if CONFIG_COMPILER_STACK_MIRROR
typedef struct {
uint32_t panicing;
uint32_t depth;
uint32_t backtrace[CONFIG_COMPILER_STACK_MIRROR_DEPTH];
} esp_stack_mirror_t;
esp_stack_mirror_t *pvTaskGetStackMirrorPointer();
#endif // CONFIG_COMPILER_STACK_MIRROR
/* When using trace macros it is sometimes necessary to include task.h before
* FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined,
* so the following two prototypes will cause a compilation error. This can be

View File

@ -15,6 +15,7 @@
#include "esp_task.h"
#include "esp_private/crosscore_int.h"
#include "esp_private/startup_internal.h" /* Required by g_spiram_ok. [refactor-todo] for g_spiram_ok */
#include "esp_private/stack_mirror.h"
#include "esp_log.h"
#include "soc/soc_memory_types.h"
#include "soc/dport_access.h"
@ -138,6 +139,11 @@ static void main_task(void* args)
}
#endif
// Todo: this could be initialized sooner, but not sure where exactly...
#if CONFIG_COMPILER_STACK_MIRROR
stack_mirror_enable();
#endif
app_main();
vTaskDelete(NULL);
}

View File

@ -558,6 +558,9 @@ static inline void __attribute__((always_inline)) uxPortCompareSetExtram(volatil
// --------------------- Interrupts ------------------------
#if CONFIG_COMPILER_STACK_MIRROR
IRAM_ATTR __attribute__((no_instrument_function))
#endif
static inline UBaseType_t __attribute__((always_inline)) xPortSetInterruptMaskFromISR(void)
{
UBaseType_t prev_int_level = XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL);
@ -565,6 +568,9 @@ static inline UBaseType_t __attribute__((always_inline)) xPortSetInterruptMaskFr
return prev_int_level;
}
#if CONFIG_COMPILER_STACK_MIRROR
IRAM_ATTR __attribute__((no_instrument_function))
#endif
static inline void __attribute__((always_inline)) vPortClearInterruptMaskFromISR(UBaseType_t prev_level)
{
portbenchmarkINTERRUPT_RESTORE(prev_level);
@ -629,6 +635,9 @@ static inline bool IRAM_ATTR xPortCanYield(void)
// ----------------------- System --------------------------
#if CONFIG_COMPILER_STACK_MIRROR
__attribute__((no_instrument_function))
#endif
static inline BaseType_t IRAM_ATTR xPortGetCoreID(void)
{
return (uint32_t) cpu_hal_get_core_id();

View File

@ -270,6 +270,7 @@
#define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL
#endif
/*
* Task control block. A task control block (TCB) is allocated for each task,
* and stores task state information, including a pointer to the task's context
@ -356,6 +357,11 @@ typedef struct tskTaskControlBlock /* The old naming convention is used to
#if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno;
#endif
#if CONFIG_COMPILER_STACK_MIRROR
esp_stack_mirror_t stackMirror;
#endif
} tskTCB;
/* The old tskTCB name is maintained above then typedefed to the new TCB_t name
@ -996,6 +1002,11 @@ static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
}
#endif /* portSTACK_GROWTH */
#if CONFIG_COMPILER_STACK_MIRROR
pxNewTCB->stackMirror.panicing = false;
pxNewTCB->stackMirror.depth = 0;
#endif
/* Store the task name in the TCB. */
if( pcName != NULL )
{
@ -4209,6 +4220,16 @@ static portTASK_FUNCTION( prvIdleTask, pvParameters )
#endif /* configUSE_TICKLESS_IDLE */
/*-----------------------------------------------------------*/
#if CONFIG_COMPILER_STACK_MIRROR
IRAM_ATTR __attribute__((no_instrument_function))
esp_stack_mirror_t * pvTaskGetStackMirrorPointer()
{
TCB_t *pxTCB = xTaskGetCurrentTaskHandle();
return &pxTCB->stackMirror;
}
#endif
#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 )
#if ( configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS )
@ -4758,6 +4779,9 @@ static void prvResetNextTaskUnblockTime( void )
#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) || (configNUM_CORES > 1) )
#if CONFIG_COMPILER_STACK_MIRROR
IRAM_ATTR __attribute__((no_instrument_function))
#endif
TaskHandle_t xTaskGetCurrentTaskHandle( void )
{
TaskHandle_t xReturn;

View File

@ -29,6 +29,9 @@
extern "C" {
#endif
#if CONFIG_COMPILER_STACK_MIRROR
IRAM_ATTR __attribute__((no_instrument_function))
#endif
static inline uint32_t IRAM_ATTR cpu_ll_get_core_id(void)
{
uint32_t id;

View File

@ -28,6 +28,9 @@
extern "C" {
#endif
#if CONFIG_COMPILER_STACK_MIRROR
__attribute__((no_instrument_function))
#endif
static inline uint32_t IRAM_ATTR cpu_ll_get_core_id(void)
{
uint32_t id;