freertos栈溢出检测原理

  • portSTACK_GROWTH > 0:栈向上生长
  • portSTACK_GROWTH < 0: 栈向下生长,本章使用栈向下生长

打开 configRECORD_STACK_HIGH_ADDRESS 1,将PCB中将打开pxEndOfStack,结构如下。

adress:0+ulStackDepth       ------------------- pxEndOfStack(栈底)
                            |XXXXXXXXXXXXXXXXXX|
adress:0+x                  --------------------pxTopOfStack(栈顶)    / StkCur(当前栈)
                            |                  |
                            |                  |
                            |                  |
adress:0                    ------------------- pxStack / pxTaskStatus->pxStackBase / StkBot

栈溢出检测原理

configCHECK_FOR_STACK_OVERFLOW 1

#if ( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) )

/* Only the current stack state is to be checked. */
    #define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
    {                                                                                                 \
        /* Is the currently saved stack pointer within the stack limit? */                            \
        if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack )                                     \
        {                                                                                             \
            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
        }                                                                                             \
    }
#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */

因为栈是向下生长的,所有判断pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack即表示溢出。

configCHECK_FOR_STACK_OVERFLOW > 1

打开tskSET_NEW_STACKS_TO_KNOWN_VALUE =1

#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) )
    #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    1
#else
    #define tskSET_NEW_STACKS_TO_KNOWN_VALUE    0
#endif

将栈填充为 0xa5

prvInitialiseNewTask
       #if ( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 )
        {
            /* Fill the stack with a known value to assist debugging. */
            ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
        }
    #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */

通过比较栈顶的值是否不等于0xa5a5a5a5来判断栈溢出,前面讲栈都填充为0xa5。

#if ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) )

    #define taskCHECK_FOR_STACK_OVERFLOW()                                                            \
    {                                                                                                 \
        const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack;                       \
        const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5;                                        \
                                                                                                      \
        if( ( pulStack[ 0 ] != ulCheckValue ) ||                                                      \
            ( pulStack[ 1 ] != ulCheckValue ) ||                                                      \
            ( pulStack[ 2 ] != ulCheckValue ) ||                                                      \
            ( pulStack[ 3 ] != ulCheckValue ) )                                                       \
        {                                                                                             \
            vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \
        }                                                                                             \
    }

#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */
/*-----------------------------------------------------------*/

所以两种检测方法,第一种是通过比较pxTopOfStack的是否小于起始地址,第二种是看填充的数据是否被修改。