diff --git a/common/inc/tx_api.h b/common/inc/tx_api.h index ed1f82918..10f360b99 100644 --- a/common/inc/tx_api.h +++ b/common/inc/tx_api.h @@ -9,8 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -/* Portions of this file were generated with AI assistance. */ - /**************************************************************************/ /**************************************************************************/ @@ -1962,7 +1960,11 @@ VOID _tx_misra_thread_entry_exit_notify_not_used(VOID (*threa #define TX_TIMER_POINTER_DIF(a,b) _tx_misra_timer_pointer_dif((TX_TIMER_INTERNAL **) (a), (TX_TIMER_INTERNAL **) (b)) #define TX_TIMER_POINTER_ADD(a,b) _tx_misra_timer_pointer_add((TX_TIMER_INTERNAL **) (a), (ULONG) (b)) #define TX_USER_TIMER_POINTER_GET(a,b) _tx_misra_user_timer_pointer_get((TX_TIMER_INTERNAL *) (a), (TX_TIMER **) &(b)); +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +#define TX_THREAD_STACK_CHECK(a) _tx_misra_thread_stack_check((a), (((a) && ((a)->tx_thread_module_instance_ptr != TX_NULL) && ((a)->tx_thread_module_kernel_stack_start != TX_NULL) && (((a)->tx_thread_module_current_user_mode == 0) || ((a)->tx_thread_stack_start == (a)->tx_thread_module_kernel_stack_start))) ? &((a)->tx_thread_module_kernel_stack_highest_ptr) : &((a)->tx_thread_stack_highest_ptr))); +#else #define TX_THREAD_STACK_CHECK(a) _tx_misra_thread_stack_check((a), &((a)->tx_thread_stack_highest_ptr)); +#endif #ifdef TX_ENABLE_EVENT_TRACE #define TX_TRACE_IN_LINE_INSERT(i,a,b,c,d,e) _tx_misra_trace_event_insert((ULONG) (i), (VOID *) (a), (ULONG) (b), (ULONG) (c), (ULONG) (d), (ULONG) (e), ((ULONG) TX_TRACE_TIME_SOURCE)); #endif diff --git a/common/inc/tx_thread.h b/common/inc/tx_thread.h index 5ba8b3f54..439a4c8c3 100644 --- a/common/inc/tx_thread.h +++ b/common/inc/tx_thread.h @@ -206,7 +206,59 @@ low address to high address (the default logic is for stacks that grow from high address to low address. */ +#ifndef tx_thread_module_kernel_stack_highest_ptr +#define tx_thread_module_kernel_stack_highest_ptr tx_thread_module_reserved +#endif + #ifndef TX_THREAD_STACK_CHECK +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +#define TX_THREAD_STACK_CHECK(thread_ptr) \ + { \ + TX_INTERRUPT_SAVE_AREA \ + TX_DISABLE \ + if (((thread_ptr)) && ((thread_ptr) -> tx_thread_id == TX_THREAD_ID)) \ + { \ + VOID **_tx_highest_ptr_ref; \ + VOID *_tx_guard_ptr; \ + VOID *_tx_stack_start; \ + if (((thread_ptr) -> tx_thread_module_instance_ptr != TX_NULL) && \ + ((thread_ptr) -> tx_thread_module_kernel_stack_start != TX_NULL) && \ + (((thread_ptr) -> tx_thread_module_current_user_mode == 0) || \ + ((thread_ptr) -> tx_thread_stack_start == (thread_ptr) -> tx_thread_module_kernel_stack_start))) \ + { \ + _tx_stack_start = (thread_ptr) -> tx_thread_module_kernel_stack_start; \ + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_module_kernel_stack_highest_ptr); \ + _tx_guard_ptr = (VOID *) (((ALIGN_TYPE) (thread_ptr) -> tx_thread_module_kernel_stack_end) + sizeof(ULONG)); \ + } \ + else \ + { \ + _tx_stack_start = (thread_ptr) -> tx_thread_stack_start; \ + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_stack_highest_ptr); \ + _tx_guard_ptr = (VOID *) (((UCHAR *) (thread_ptr) -> tx_thread_stack_end) + 1); \ + } \ + if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *_tx_highest_ptr_ref)) \ + { \ + *_tx_highest_ptr_ref = (thread_ptr) -> tx_thread_stack_ptr; \ + } \ + if ((*((ULONG *) _tx_stack_start) != TX_STACK_FILL) || \ + (*((ULONG *) _tx_guard_ptr) != TX_STACK_FILL) || \ + (((ULONG *) *_tx_highest_ptr_ref) < ((ULONG *) _tx_stack_start))) \ + { \ + TX_RESTORE \ + _tx_thread_stack_error_handler((thread_ptr)); \ + TX_DISABLE \ + } \ + else if ((((ULONG *) *_tx_highest_ptr_ref) > ((ULONG *) _tx_stack_start)) && \ + (*(((ULONG *) *_tx_highest_ptr_ref) - 1) != TX_STACK_FILL)) \ + { \ + TX_RESTORE \ + _tx_thread_stack_analyze((thread_ptr)); \ + TX_DISABLE \ + } \ + } \ + TX_RESTORE \ + } +#else #define TX_THREAD_STACK_CHECK(thread_ptr) \ { \ TX_INTERRUPT_SAVE_AREA \ @@ -225,7 +277,8 @@ _tx_thread_stack_error_handler((thread_ptr)); \ TX_DISABLE \ } \ - else if (*(((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) - 1) != TX_STACK_FILL) \ + else if ((((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) > ((ULONG *) (thread_ptr) -> tx_thread_stack_start)) && \ + (*(((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) - 1) != TX_STACK_FILL)) \ { \ TX_RESTORE \ _tx_thread_stack_analyze((thread_ptr)); \ @@ -235,6 +288,7 @@ TX_RESTORE \ } #endif +#endif /* Define default post thread delete macro to whitespace, if it hasn't been defined previously (typically in tx_port.h). */ diff --git a/common/src/tx_misra.c b/common/src/tx_misra.c index 7ac5dc68c..1a7509a06 100644 --- a/common/src/tx_misra.c +++ b/common/src/tx_misra.c @@ -276,10 +276,51 @@ VOID _tx_misra_thread_stack_check(TX_THREAD *thread_ptr, VOID **highest_stack) { TX_INTERRUPT_SAVE_AREA +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +VOID *_tx_guard_ptr; +VOID *_tx_stack_start; +VOID **_tx_highest_ptr_ref; +#endif TX_DISABLE if (((thread_ptr)) && ((thread_ptr) -> tx_thread_id == TX_THREAD_ID)) { +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + if (((thread_ptr) -> tx_thread_module_instance_ptr != TX_NULL) && + ((thread_ptr) -> tx_thread_module_kernel_stack_start != TX_NULL) && + (((thread_ptr) -> tx_thread_module_current_user_mode == 0) || + ((thread_ptr) -> tx_thread_stack_start == (thread_ptr) -> tx_thread_module_kernel_stack_start))) + { + _tx_stack_start = (thread_ptr) -> tx_thread_module_kernel_stack_start; + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_module_kernel_stack_highest_ptr); + _tx_guard_ptr = (VOID *) (((ALIGN_TYPE) (thread_ptr) -> tx_thread_module_kernel_stack_end) + sizeof(ULONG)); + } + else + { + _tx_stack_start = (thread_ptr) -> tx_thread_stack_start; + _tx_highest_ptr_ref = highest_stack; + _tx_guard_ptr = (VOID *) (((UCHAR *) (thread_ptr) -> tx_thread_stack_end) + 1); + } + if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *_tx_highest_ptr_ref)) + { + *_tx_highest_ptr_ref = (thread_ptr) -> tx_thread_stack_ptr; + } + if ((*((ULONG *) _tx_stack_start) != TX_STACK_FILL) || + (*((ULONG *) _tx_guard_ptr) != TX_STACK_FILL) || + (((ULONG *) *_tx_highest_ptr_ref) < ((ULONG *) _tx_stack_start))) + { + TX_RESTORE + _tx_thread_stack_error_handler((thread_ptr)); + TX_DISABLE + } + else if ((((ULONG *) *_tx_highest_ptr_ref) > ((ULONG *) _tx_stack_start)) && + (*(((ULONG *) *_tx_highest_ptr_ref) - 1) != TX_STACK_FILL)) + { + TX_RESTORE + _tx_thread_stack_analyze((thread_ptr)); + TX_DISABLE + } +#else if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *highest_stack)) { *highest_stack = (thread_ptr) -> tx_thread_stack_ptr; @@ -292,12 +333,14 @@ TX_INTERRUPT_SAVE_AREA _tx_thread_stack_error_handler((thread_ptr)); TX_DISABLE } - if (*(((ULONG *) *highest_stack) - 1) != TX_STACK_FILL) + else if ((((ULONG *) *highest_stack) > ((ULONG *) (thread_ptr) -> tx_thread_stack_start)) && + (*(((ULONG *) *highest_stack) - 1) != TX_STACK_FILL)) { TX_RESTORE _tx_thread_stack_analyze((thread_ptr)); TX_DISABLE } +#endif } TX_RESTORE } diff --git a/common/src/tx_thread_stack_analyze.c b/common/src/tx_thread_stack_analyze.c index ecd7ee13b..000e44a47 100644 --- a/common/src/tx_thread_stack_analyze.c +++ b/common/src/tx_thread_stack_analyze.c @@ -9,8 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -// Portions of this file were generated with AI assistance. - /**************************************************************************/ /**************************************************************************/ @@ -77,6 +75,9 @@ ULONG *stack_limit; ULONG probe_count; UINT fill_present; ULONG size; +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +UINT analyzing_kernel_stack; +#endif /* Disable interrupts. */ @@ -97,8 +98,25 @@ ULONG size; if (stack_lowest != TX_NULL) { +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + /* Determine if this thread is currently on the module kernel stack. */ + if (((thread_ptr -> tx_thread_module_kernel_stack_start) != TX_NULL) && + (((thread_ptr -> tx_thread_module_current_user_mode) == 0) || + ((thread_ptr -> tx_thread_stack_start) == (thread_ptr -> tx_thread_module_kernel_stack_start)))) + { + analyzing_kernel_stack = TX_TRUE; + stack_lowest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_start); + stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_highest_ptr); + } + else + { + analyzing_kernel_stack = TX_FALSE; + stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); + } +#else /* Pickup the highest stack pointer. */ stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); +#endif /* Determine if the pointer is null or if the highest stack pointer is not above the start of the stack. The latter indicates a stack overflow or a corrupted thread @@ -199,6 +217,34 @@ ULONG size; /* Yes, thread is still created. */ +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + if (analyzing_kernel_stack == TX_TRUE) + { + if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_start))) + { + if (stack_ptr < (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_highest_ptr))) + { + if (*stack_ptr != TX_STACK_FILL) + { + thread_ptr -> tx_thread_module_kernel_stack_highest_ptr = (VOID *) stack_ptr; + } + } + } + } + else + { + if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_start))) + { + if (stack_ptr < (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr))) + { + if (*stack_ptr != TX_STACK_FILL) + { + thread_ptr -> tx_thread_stack_highest_ptr = stack_ptr; + } + } + } + } +#else /* Now check the new highest stack pointer is past the stack start. */ if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_start))) { @@ -216,6 +262,7 @@ ULONG size; } } } +#endif } } } diff --git a/common_modules/inc/txm_module.h b/common_modules/inc/txm_module.h index a823c2c05..97ffb3a17 100644 --- a/common_modules/inc/txm_module.h +++ b/common_modules/inc/txm_module.h @@ -182,6 +182,11 @@ extern "C" { #endif +/* Define alias for kernel stack highest pointer used when stack checking is enabled. */ + +#define tx_thread_module_kernel_stack_highest_ptr tx_thread_module_reserved + + /* Define object types for object search requests. */ #define TXM_BLOCK_POOL_OBJECT 1 diff --git a/common_modules/module_manager/src/txm_module_manager_thread_create.c b/common_modules/module_manager/src/txm_module_manager_thread_create.c index 51b85e1b4..58cdd953a 100644 --- a/common_modules/module_manager/src/txm_module_manager_thread_create.c +++ b/common_modules/module_manager/src/txm_module_manager_thread_create.c @@ -9,8 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -// Portions of this file were generated with AI assistance. - /**************************************************************************/ /**************************************************************************/ @@ -327,13 +325,6 @@ ULONG i; thread_ptr -> tx_thread_stack_fill_value = stack_fill_value; #endif - /* Note that TX_ENABLE_STACK_CHECKING is not supported for module threads. A user mode - module thread owns two stacks and the scheduler swaps tx_thread_stack_start, - tx_thread_stack_end and tx_thread_stack_size over to the kernel stack whenever the - thread enters the module manager. Neither tx_thread_stack_highest_ptr nor the guard - pattern beyond the end of the stack is maintained across that switch, so - TX_THREAD_STACK_CHECK may report spurious stack errors for module threads. */ - /* If the thread runs on user mode, allocate the kernel stack for syscall. */ if((module_instance -> txm_module_instance_property_flags) & TXM_MODULE_USER_MODE) { @@ -354,12 +345,21 @@ ULONG i; #endif /* Align kernel stack pointer. */ +#ifdef TX_ENABLE_STACK_CHECKING + thread_ptr -> tx_thread_module_kernel_stack_end = (VOID *) (((ALIGN_TYPE)(thread_ptr -> tx_thread_module_kernel_stack_start) + TXM_MODULE_KERNEL_STACK_SIZE - (sizeof(ULONG) * 2)) & ~0x07); +#else thread_ptr -> tx_thread_module_kernel_stack_end = (VOID *) (((ALIGN_TYPE)(thread_ptr -> tx_thread_module_kernel_stack_start) + TXM_MODULE_KERNEL_STACK_SIZE) & ~0x07); +#endif /* Set kernel stack size. Aligning the end of the kernel stack downwards can consume up to seven bytes of the block returned by the allocator, so the usable size must be derived from the aligned range instead of from TXM_MODULE_KERNEL_STACK_SIZE. */ thread_ptr -> tx_thread_module_kernel_stack_size = (ULONG) ((ALIGN_TYPE) (thread_ptr -> tx_thread_module_kernel_stack_end) - (ALIGN_TYPE) (thread_ptr -> tx_thread_module_kernel_stack_start)); + +#ifdef TX_ENABLE_STACK_CHECKING + /* Setup the highest usage pointer for the kernel stack. */ + thread_ptr -> tx_thread_module_kernel_stack_highest_ptr = thread_ptr -> tx_thread_module_kernel_stack_end; +#endif } #if TXM_MODULE_MEMORY_PROTECTION diff --git a/common_smp/inc/tx_api.h b/common_smp/inc/tx_api.h index 77bd6218b..0188c547c 100644 --- a/common_smp/inc/tx_api.h +++ b/common_smp/inc/tx_api.h @@ -9,8 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -/* Portions of this file were generated with AI assistance. */ - /**************************************************************************/ /**************************************************************************/ @@ -1971,7 +1969,11 @@ VOID _tx_misra_thread_entry_exit_notify_not_used(VOID (*threa #define TX_TIMER_POINTER_DIF(a,b) _tx_misra_timer_pointer_dif((TX_TIMER_INTERNAL **) (a), (TX_TIMER_INTERNAL **) (b)) #define TX_TIMER_POINTER_ADD(a,b) _tx_misra_timer_pointer_add((TX_TIMER_INTERNAL **) (a), (ULONG) (b)) #define TX_USER_TIMER_POINTER_GET(a,b) _tx_misra_user_timer_pointer_get((TX_TIMER_INTERNAL *) (a), (TX_TIMER **) &(b)); +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +#define TX_THREAD_STACK_CHECK(a) _tx_misra_thread_stack_check((a), (((a) && ((a)->tx_thread_module_instance_ptr != TX_NULL) && ((a)->tx_thread_module_kernel_stack_start != TX_NULL) && (((a)->tx_thread_module_current_user_mode == 0) || ((a)->tx_thread_stack_start == (a)->tx_thread_module_kernel_stack_start))) ? &((a)->tx_thread_module_kernel_stack_highest_ptr) : &((a)->tx_thread_stack_highest_ptr))); +#else #define TX_THREAD_STACK_CHECK(a) _tx_misra_thread_stack_check((a), &((a)->tx_thread_stack_highest_ptr)); +#endif #ifdef TX_ENABLE_EVENT_TRACE #define TX_TRACE_IN_LINE_INSERT(i,a,b,c,d,e) _tx_misra_trace_event_insert((ULONG) (i), (VOID *) (a), (ULONG) (b), (ULONG) (c), (ULONG) (d), (ULONG) (e), ((ULONG) TX_TRACE_TIME_SOURCE)); #endif diff --git a/common_smp/inc/tx_thread.h b/common_smp/inc/tx_thread.h index 1ffe88287..eb653ab33 100644 --- a/common_smp/inc/tx_thread.h +++ b/common_smp/inc/tx_thread.h @@ -9,7 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -// Portions of this file were generated with AI assistance. /**************************************************************************/ /**************************************************************************/ @@ -64,7 +63,59 @@ low address to high address (the default logic is for stacks that grow from high address to low address. */ +#ifndef tx_thread_module_kernel_stack_highest_ptr +#define tx_thread_module_kernel_stack_highest_ptr tx_thread_module_reserved +#endif + #ifndef TX_THREAD_STACK_CHECK +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +#define TX_THREAD_STACK_CHECK(thread_ptr) \ + { \ + TX_INTERRUPT_SAVE_AREA \ + TX_DISABLE \ + if (((thread_ptr)) && ((thread_ptr) -> tx_thread_id == TX_THREAD_ID)) \ + { \ + VOID **_tx_highest_ptr_ref; \ + VOID *_tx_guard_ptr; \ + VOID *_tx_stack_start; \ + if (((thread_ptr) -> tx_thread_module_instance_ptr != TX_NULL) && \ + ((thread_ptr) -> tx_thread_module_kernel_stack_start != TX_NULL) && \ + (((thread_ptr) -> tx_thread_module_current_user_mode == 0) || \ + ((thread_ptr) -> tx_thread_stack_start == (thread_ptr) -> tx_thread_module_kernel_stack_start))) \ + { \ + _tx_stack_start = (thread_ptr) -> tx_thread_module_kernel_stack_start; \ + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_module_kernel_stack_highest_ptr); \ + _tx_guard_ptr = (VOID *) (((ALIGN_TYPE) (thread_ptr) -> tx_thread_module_kernel_stack_end) + sizeof(ULONG)); \ + } \ + else \ + { \ + _tx_stack_start = (thread_ptr) -> tx_thread_stack_start; \ + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_stack_highest_ptr); \ + _tx_guard_ptr = (VOID *) (((UCHAR *) (thread_ptr) -> tx_thread_stack_end) + 1); \ + } \ + if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *_tx_highest_ptr_ref)) \ + { \ + *_tx_highest_ptr_ref = (thread_ptr) -> tx_thread_stack_ptr; \ + } \ + if ((*((ULONG *) _tx_stack_start) != TX_STACK_FILL) || \ + (*((ULONG *) _tx_guard_ptr) != TX_STACK_FILL) || \ + (((ULONG *) *_tx_highest_ptr_ref) < ((ULONG *) _tx_stack_start))) \ + { \ + TX_RESTORE \ + _tx_thread_stack_error_handler((thread_ptr)); \ + TX_DISABLE \ + } \ + else if ((((ULONG *) *_tx_highest_ptr_ref) > ((ULONG *) _tx_stack_start)) && \ + (*(((ULONG *) *_tx_highest_ptr_ref) - 1) != TX_STACK_FILL)) \ + { \ + TX_RESTORE \ + _tx_thread_stack_analyze((thread_ptr)); \ + TX_DISABLE \ + } \ + } \ + TX_RESTORE \ + } +#else #define TX_THREAD_STACK_CHECK(thread_ptr) \ { \ TX_INTERRUPT_SAVE_AREA \ @@ -83,7 +134,8 @@ _tx_thread_stack_error_handler((thread_ptr)); \ TX_DISABLE \ } \ - else if (*(((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) - 1) != TX_STACK_FILL) \ + else if ((((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) > ((ULONG *) (thread_ptr) -> tx_thread_stack_start)) && \ + (*(((ULONG *) (thread_ptr) -> tx_thread_stack_highest_ptr) - 1) != TX_STACK_FILL)) \ { \ TX_RESTORE \ _tx_thread_stack_analyze((thread_ptr)); \ @@ -93,6 +145,7 @@ TX_RESTORE \ } #endif +#endif /* Define default post thread delete macro to whitespace, if it hasn't been defined previously (typically in tx_port.h). */ diff --git a/common_smp/src/tx_misra.c b/common_smp/src/tx_misra.c index b9144a70c..a22c0ec7c 100644 --- a/common_smp/src/tx_misra.c +++ b/common_smp/src/tx_misra.c @@ -274,10 +274,51 @@ VOID _tx_misra_thread_stack_check(TX_THREAD *thread_ptr, VOID **highest_stack) { TX_INTERRUPT_SAVE_AREA +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +VOID *_tx_guard_ptr; +VOID *_tx_stack_start; +VOID **_tx_highest_ptr_ref; +#endif TX_DISABLE if (((thread_ptr)) && ((thread_ptr) -> tx_thread_id == TX_THREAD_ID)) { +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + if (((thread_ptr) -> tx_thread_module_instance_ptr != TX_NULL) && + ((thread_ptr) -> tx_thread_module_kernel_stack_start != TX_NULL) && + (((thread_ptr) -> tx_thread_module_current_user_mode == 0) || + ((thread_ptr) -> tx_thread_stack_start == (thread_ptr) -> tx_thread_module_kernel_stack_start))) + { + _tx_stack_start = (thread_ptr) -> tx_thread_module_kernel_stack_start; + _tx_highest_ptr_ref = &((thread_ptr) -> tx_thread_module_kernel_stack_highest_ptr); + _tx_guard_ptr = (VOID *) (((ALIGN_TYPE) (thread_ptr) -> tx_thread_module_kernel_stack_end) + sizeof(ULONG)); + } + else + { + _tx_stack_start = (thread_ptr) -> tx_thread_stack_start; + _tx_highest_ptr_ref = highest_stack; + _tx_guard_ptr = (VOID *) (((UCHAR *) (thread_ptr) -> tx_thread_stack_end) + 1); + } + if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *_tx_highest_ptr_ref)) + { + *_tx_highest_ptr_ref = (thread_ptr) -> tx_thread_stack_ptr; + } + if ((*((ULONG *) _tx_stack_start) != TX_STACK_FILL) || + (*((ULONG *) _tx_guard_ptr) != TX_STACK_FILL) || + (((ULONG *) *_tx_highest_ptr_ref) < ((ULONG *) _tx_stack_start))) + { + TX_RESTORE + _tx_thread_stack_error_handler((thread_ptr)); + TX_DISABLE + } + else if ((((ULONG *) *_tx_highest_ptr_ref) > ((ULONG *) _tx_stack_start)) && + (*(((ULONG *) *_tx_highest_ptr_ref) - 1) != TX_STACK_FILL)) + { + TX_RESTORE + _tx_thread_stack_analyze((thread_ptr)); + TX_DISABLE + } +#else if (((ULONG *) (thread_ptr) -> tx_thread_stack_ptr) < ((ULONG *) *highest_stack)) { *highest_stack = (thread_ptr) -> tx_thread_stack_ptr; @@ -290,12 +331,14 @@ TX_INTERRUPT_SAVE_AREA _tx_thread_stack_error_handler((thread_ptr)); TX_DISABLE } - if (*(((ULONG *) *highest_stack) - 1) != TX_STACK_FILL) + else if ((((ULONG *) *highest_stack) > ((ULONG *) (thread_ptr) -> tx_thread_stack_start)) && + (*(((ULONG *) *highest_stack) - 1) != TX_STACK_FILL)) { TX_RESTORE _tx_thread_stack_analyze((thread_ptr)); TX_DISABLE } +#endif } TX_RESTORE } diff --git a/common_smp/src/tx_thread_stack_analyze.c b/common_smp/src/tx_thread_stack_analyze.c index ecd7ee13b..dd3dd37c5 100644 --- a/common_smp/src/tx_thread_stack_analyze.c +++ b/common_smp/src/tx_thread_stack_analyze.c @@ -9,8 +9,6 @@ * SPDX-License-Identifier: MIT **************************************************************************/ -// Portions of this file were generated with AI assistance. - /**************************************************************************/ /**************************************************************************/ @@ -77,6 +75,9 @@ ULONG *stack_limit; ULONG probe_count; UINT fill_present; ULONG size; +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) +UINT analyzing_kernel_stack; +#endif /* Disable interrupts. */ @@ -97,8 +98,25 @@ ULONG size; if (stack_lowest != TX_NULL) { +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + /* Determine if this thread is currently on the module kernel stack. */ + if (((thread_ptr -> tx_thread_module_kernel_stack_start) != TX_NULL) && + (((thread_ptr -> tx_thread_module_current_user_mode) == 0) || + ((thread_ptr -> tx_thread_stack_start) == (thread_ptr -> tx_thread_module_kernel_stack_start)))) + { + analyzing_kernel_stack = TX_TRUE; + stack_lowest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_start); + stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_highest_ptr); + } + else + { + analyzing_kernel_stack = TX_FALSE; + stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); + } +#else /* Pickup the highest stack pointer. */ stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); +#endif /* Determine if the pointer is null or if the highest stack pointer is not above the start of the stack. The latter indicates a stack overflow or a corrupted thread @@ -199,10 +217,37 @@ ULONG size; /* Yes, thread is still created. */ +#if defined(TXM_MODULE_MANAGER) && defined(TX_ENABLE_STACK_CHECKING) + if (analyzing_kernel_stack == TX_TRUE) + { + if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_start))) + { + if (stack_ptr < (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_module_kernel_stack_highest_ptr))) + { + if (*stack_ptr != TX_STACK_FILL) + { + thread_ptr -> tx_thread_module_kernel_stack_highest_ptr = (VOID *) stack_ptr; + } + } + } + } + else + { + if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_start))) + { + if (stack_ptr < (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr))) + { + if (*stack_ptr != TX_STACK_FILL) + { + thread_ptr -> tx_thread_stack_highest_ptr = stack_ptr; + } + } + } + } +#else /* Now check the new highest stack pointer is past the stack start. */ if (stack_ptr > (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_start))) { - /* Yes, now check that the new highest stack pointer is less than the previous highest stack pointer. */ if (stack_ptr < (TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr))) { @@ -216,6 +261,7 @@ ULONG size; } } } +#endif } } } diff --git a/test/tx/cmake/module_manager/CMakeLists.txt b/test/tx/cmake/module_manager/CMakeLists.txt index 17511fd59..bdadf8fff 100644 --- a/test/tx/cmake/module_manager/CMakeLists.txt +++ b/test/tx/cmake/module_manager/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) cmake_policy(SET CMP0057 NEW) project(module_manager_test LANGUAGES C) +enable_testing() set(REPO_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../..) set(SOURCE_DIR ${REPO_ROOT}/test/tx/module_manager) @@ -252,3 +253,35 @@ target_include_directories( add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_cortex_a7_range_check_test threadx_module_manager_cortex_a7_range_check_test) + +add_executable( + threadx_module_manager_thread_stack_checking_test + ${SOURCE_DIR}/threadx_module_manager_thread_stack_checking_test.c + ${module_manager_dir}/src/txm_module_manager_thread_create.c + ${module_manager_dir}/src/txm_module_manager_object_allocate.c + ${module_manager_dir}/src/txm_module_manager_object_deallocate.c + ${module_manager_dir}/src/txm_module_manager_util.c + ${REPO_ROOT}/common/src/tx_thread_stack_analyze.c) + +target_include_directories( + threadx_module_manager_thread_stack_checking_test + PRIVATE ${SOURCE_DIR} + ${REPO_ROOT}/common/inc + ${REPO_ROOT}/common_modules/inc + ${module_manager_dir}/inc + ${cortex_a7_module_dir}/inc) + +target_compile_definitions( + threadx_module_manager_thread_stack_checking_test + PRIVATE TXM_MODULE_MANAGER + TX_ENABLE_STACK_CHECKING) + +target_compile_options( + threadx_module_manager_thread_stack_checking_test + PRIVATE -Wall + -Wextra + -include ${SOURCE_DIR}/threadx_module_manager_host_test_port.h) + +add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_thread_stack_checking_test + threadx_module_manager_thread_stack_checking_test) + diff --git a/test/tx/module_manager/threadx_module_manager_host_test_port.h b/test/tx/module_manager/threadx_module_manager_host_test_port.h index 36607d206..7752329a3 100644 --- a/test/tx/module_manager/threadx_module_manager_host_test_port.h +++ b/test/tx/module_manager/threadx_module_manager_host_test_port.h @@ -5,12 +5,7 @@ * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * - * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5). - * The AI-generated portions may be considered public domain (CC0-1.0) - * and not subject to the project's licence. The human contributor has - * reviewed and verified that the code is correct. - * - * SPDX-License-Identifier: MIT and CC0-1.0 + * SPDX-License-Identifier: MIT **************************************************************************/ diff --git a/test/tx/module_manager/threadx_module_manager_thread_stack_checking_test.c b/test/tx/module_manager/threadx_module_manager_thread_stack_checking_test.c new file mode 100644 index 000000000..308674320 --- /dev/null +++ b/test/tx/module_manager/threadx_module_manager_thread_stack_checking_test.c @@ -0,0 +1,451 @@ +/***************************************************************************/ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Test */ +/** */ +/** Module Manager thread stack checking test */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#include + +#include "threadx_module_manager_host_test_port.h" + +#include "tx_thread.h" +#include "tx_timer.h" +#include "tx_queue.h" +#include "tx_semaphore.h" +#include "tx_mutex.h" +#include "tx_event_flags.h" +#include "tx_block_pool.h" +#include "tx_byte_pool.h" +#include "txm_module.h" +#include "txm_module_manager_util.h" + +/* Prototype for direct stack analyze call. */ +VOID _tx_thread_stack_analyze(TX_THREAD *thread_ptr); + +/* Stand-in interrupt locks for the test port. */ +unsigned int test_interrupt_disable_depth; +unsigned int test_interrupt_disable_max_depth; +unsigned int test_interrupt_restore_underflows; + +/* Manager globals. */ +TX_BYTE_POOL _txm_module_manager_object_pool; +UINT _txm_module_manager_object_pool_created; +TX_MUTEX _txm_module_manager_mutex; +TX_THREAD *_tx_thread_current_ptr; + +/* Kernel created object lists. */ +TX_BLOCK_POOL *_tx_block_pool_created_ptr; +ULONG _tx_block_pool_created_count; +TX_BYTE_POOL *_tx_byte_pool_created_ptr; +ULONG _tx_byte_pool_created_count; +TX_EVENT_FLAGS_GROUP *_tx_event_flags_created_ptr; +ULONG _tx_event_flags_created_count; +TX_MUTEX *_tx_mutex_created_ptr; +ULONG _tx_mutex_created_count; +TX_QUEUE *_tx_queue_created_ptr; +ULONG _tx_queue_created_count; +TX_SEMAPHORE *_tx_semaphore_created_ptr; +ULONG _tx_semaphore_created_count; +TX_THREAD *_tx_thread_created_ptr; +ULONG _tx_thread_created_count; +TX_TIMER *_tx_timer_created_ptr; +ULONG _tx_timer_created_count; + +TX_THREAD *_tx_thread_execute_ptr; +volatile UINT _tx_thread_preempt_disable; +volatile ULONG _tx_thread_system_state; +TX_THREAD _tx_timer_thread; + +VOID _tx_thread_system_preempt_check(VOID) +{ +} + +VOID _tx_thread_system_resume(TX_THREAD *thread_ptr) +{ + (VOID) thread_ptr; +} + +VOID _tx_thread_timeout(ULONG timeout_input) +{ + (VOID) timeout_input; +} + +ALIGN_TYPE _txm_module_manager_kernel_dispatch(ULONG kernel_request, ALIGN_TYPE param_1, ALIGN_TYPE param_2, ALIGN_TYPE param_3) +{ + (VOID) kernel_request; + (VOID) param_1; + (VOID) param_2; + (VOID) param_3; + return(0); +} + +ALIGN_TYPE _txm_module_manager_user_mode_entry(ULONG kernel_request, ALIGN_TYPE param_1, ALIGN_TYPE param_2, ALIGN_TYPE param_3) +{ + (VOID) kernel_request; + (VOID) param_1; + (VOID) param_2; + (VOID) param_3; + return(0); +} + +#define TEST_POOL_BYTES 16384 + +static union +{ + ALIGN_TYPE test_pool_alignment; + UCHAR test_pool_bytes[TEST_POOL_BYTES]; +} test_pool_arena; + +static ULONG test_pool_offset; + +static ULONG test_checks; +static UINT test_failures; +static UINT test_stack_error_detected; + +VOID _tx_thread_stack_error_handler(TX_THREAD *thread_ptr) +{ + (VOID) thread_ptr; + test_stack_error_detected++; +} + +static VOID test_expect(const char *description, ULONG actual, ULONG expected) +{ + test_checks++; + if (actual != expected) + { + printf("FAIL: %s (expected %lu, got %lu)\n", description, + (unsigned long) expected, (unsigned long) actual); + test_failures++; + } +} + +UINT _txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option) +{ + ULONG rounded_size; + UCHAR *block_start; + + (VOID) pool_ptr; + (VOID) wait_option; + + rounded_size = (memory_size + (sizeof(ALIGN_TYPE) - 1U)) & (~(sizeof(ALIGN_TYPE) - 1U)); + if ((test_pool_offset + rounded_size) > ((ULONG) TEST_POOL_BYTES)) + { + return(TX_NO_MEMORY); + } + + block_start = &test_pool_arena.test_pool_bytes[test_pool_offset]; + test_pool_offset += rounded_size; + *memory_ptr = (VOID *) block_start; + + return(TX_SUCCESS); +} + +UINT _txe_byte_release(VOID *memory_ptr) +{ + (VOID) memory_ptr; + return(TX_SUCCESS); +} + +UINT _txe_mutex_get(TX_MUTEX *mutex_ptr, ULONG wait_option) +{ + (VOID) mutex_ptr; + (VOID) wait_option; + return(TX_SUCCESS); +} + +UINT _txe_mutex_put(TX_MUTEX *mutex_ptr) +{ + (VOID) mutex_ptr; + return(TX_SUCCESS); +} + +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, + ULONG obj_size, UINT write_request) +{ + (VOID) module_instance; + (VOID) obj_ptr; + (VOID) obj_size; + (VOID) write_request; + return(TX_FALSE); +} + +UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, + ULONG obj_size) +{ + (VOID) module_instance; + (VOID) obj_ptr; + (VOID) obj_size; + return(TX_TRUE); +} + +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size, + ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment) +{ + (VOID) module_preamble; + (VOID) code_size; + (VOID) code_alignment; + (VOID) data_size; + (VOID) data_alignment; +} + +VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, + VOID (*shell_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +{ + (VOID) shell_function; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_stack_end) - 64); +} + +static VOID dummy_shell(TX_THREAD *thread_ptr, TXM_MODULE_INSTANCE *module_instance) +{ + (VOID) thread_ptr; + (VOID) module_instance; +} + +static VOID dummy_entry(ULONG id) +{ + (VOID) id; +} + +UINT _txe_thread_create(TX_THREAD *thread_ptr, TX_NAME_CONST CHAR *name_ptr, + VOID (*entry_function)(ULONG id), ULONG entry_input, + VOID *stack_start, ULONG stack_size, + UINT priority, UINT preempt_threshold, + ULONG time_slice, UINT auto_start, UINT thread_control_block_size) +{ + (VOID) entry_function; + (VOID) entry_input; + (VOID) priority; + (VOID) preempt_threshold; + (VOID) time_slice; + (VOID) auto_start; + (VOID) thread_control_block_size; + + thread_ptr -> tx_thread_id = TX_THREAD_ID; + thread_ptr -> tx_thread_name = (CHAR *) name_ptr; + thread_ptr -> tx_thread_stack_start = stack_start; + thread_ptr -> tx_thread_stack_end = (VOID *) (((ALIGN_TYPE) stack_start) + stack_size - 1); + thread_ptr -> tx_thread_stack_size = stack_size; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) stack_start) + stack_size - 64); + thread_ptr -> tx_thread_stack_highest_ptr = thread_ptr -> tx_thread_stack_ptr; + + return(TX_SUCCESS); +} + +int main(void) +{ + TXM_MODULE_INSTANCE module_instance; + TX_THREAD thread; + TX_THREAD *thread_ptr = &thread; + UINT status; + ULONG user_stack[512]; + ULONG kernel_stack_size; + ULONG *guard_word_ptr; + VOID *saved_user_stack_start; + VOID *saved_user_stack_end; + VOID *saved_user_stack_highest_ptr; + ULONG *user_guard_ptr; + ULONG *stack_scan; + + setvbuf(stdout, TX_NULL, _IONBF, 0); + + test_failures = 0U; + test_checks = 0U; + test_pool_offset = 0U; + test_stack_error_detected = 0U; + + _txm_module_manager_object_pool.tx_byte_pool_id = TX_BYTE_POOL_ID; + _txm_module_manager_object_pool.tx_byte_pool_start = test_pool_arena.test_pool_bytes; + _txm_module_manager_object_pool.tx_byte_pool_size = TEST_POOL_BYTES; + _txm_module_manager_object_pool.tx_byte_pool_available = TEST_POOL_BYTES; + _txm_module_manager_object_pool_created = TX_TRUE; + + TX_MEMSET(&module_instance, 0, sizeof(TXM_MODULE_INSTANCE)); + module_instance.txm_module_instance_property_flags = TXM_MODULE_USER_MODE | TXM_MODULE_MEMORY_PROTECTION; + + /* Initialize user stack with fill pattern. */ + TX_MEMSET(user_stack, (int) 0xEF, sizeof(user_stack)); + *(ULONG *) user_stack = TX_STACK_FILL; + + /* Create module thread with TX_DONT_START. */ + status = _txm_module_manager_thread_create(thread_ptr, "test_thread", + dummy_shell, dummy_entry, 0, + user_stack, sizeof(user_stack), + 1, 1, TX_NO_TIME_SLICE, TX_DONT_START, + sizeof(TX_THREAD), &module_instance); + + test_expect("thread create status success", (ULONG) status, (ULONG) TX_SUCCESS); + test_expect("kernel stack allocated", (ULONG) (thread_ptr -> tx_thread_module_kernel_stack_start != TX_NULL), (ULONG) TX_TRUE); + + if (thread_ptr -> tx_thread_module_kernel_stack_start == TX_NULL) + { + printf("ERROR: Kernel stack allocation failed\n"); + return(1); + } + + saved_user_stack_start = thread_ptr -> tx_thread_stack_start; + saved_user_stack_end = thread_ptr -> tx_thread_stack_end; + saved_user_stack_highest_ptr = thread_ptr -> tx_thread_stack_highest_ptr; + user_guard_ptr = (ULONG *) (((UCHAR *) saved_user_stack_end) + 1); + + /* Ensure entire user stack and guard pattern hold TX_STACK_FILL. */ + stack_scan = (ULONG *) saved_user_stack_start; + while (stack_scan <= (ULONG *) saved_user_stack_end) + { + *stack_scan = TX_STACK_FILL; + stack_scan++; + } + *user_guard_ptr = TX_STACK_FILL; + + /* Verify kernel stack end is properly aligned and within the allocated buffer. */ + kernel_stack_size = thread_ptr -> tx_thread_module_kernel_stack_size; + test_expect("kernel stack size valid", (ULONG) (kernel_stack_size > 0), (ULONG) TX_TRUE); + + guard_word_ptr = (ULONG *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end) + sizeof(ULONG)); + + /* Ensure entire kernel stack and guard pattern hold TX_STACK_FILL. */ + stack_scan = (ULONG *) thread_ptr -> tx_thread_module_kernel_stack_start; + while (stack_scan <= (ULONG *) thread_ptr -> tx_thread_module_kernel_stack_end) + { + *stack_scan = TX_STACK_FILL; + stack_scan++; + } + *guard_word_ptr = TX_STACK_FILL; + test_expect("kernel stack guard pattern initialized", *guard_word_ptr, (ULONG) TX_STACK_FILL); + + test_expect("kernel stack highest ptr initialized to end", + (ULONG) (ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_highest_ptr, + (ULONG) (ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end); + + /* 1. Test stack checking in user mode. */ + test_stack_error_detected = 0; + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("stack check in user mode succeeds without error", (ULONG) test_stack_error_detected, 0); + + /* 2. Test transition to kernel mode. */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + thread_ptr -> tx_thread_stack_start = thread_ptr -> tx_thread_module_kernel_stack_start; + thread_ptr -> tx_thread_stack_end = thread_ptr -> tx_thread_module_kernel_stack_end; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end) - 128); + + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("stack check in kernel mode succeeds without error", (ULONG) test_stack_error_detected, 0); + test_expect("kernel stack highest ptr updated to kernel stack ptr", + (ULONG) (ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_highest_ptr, + (ULONG) (ALIGN_TYPE) thread_ptr -> tx_thread_stack_ptr); + + /* Verify user stack highest pointer was preserved and not corrupted. */ + test_expect("user stack highest ptr not overwritten by kernel stack address", + (ULONG) (ALIGN_TYPE) thread_ptr -> tx_thread_stack_highest_ptr, + (ULONG) (ALIGN_TYPE) saved_user_stack_highest_ptr); + + /* 3. Test return to user mode. */ + thread_ptr -> tx_thread_module_current_user_mode = thread_ptr -> tx_thread_module_user_mode; + thread_ptr -> tx_thread_stack_start = saved_user_stack_start; + thread_ptr -> tx_thread_stack_end = saved_user_stack_end; + thread_ptr -> tx_thread_stack_ptr = saved_user_stack_highest_ptr; + + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("stack check after returning to user mode succeeds without spurious error", (ULONG) test_stack_error_detected, 0); + + /* 4. Test stack error detection on corrupted kernel guard pattern. */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + thread_ptr -> tx_thread_stack_start = thread_ptr -> tx_thread_module_kernel_stack_start; + thread_ptr -> tx_thread_stack_end = thread_ptr -> tx_thread_module_kernel_stack_end; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end) - 128); + + test_stack_error_detected = 0; + *guard_word_ptr = 0x12345678; + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("corrupted kernel guard detected", (ULONG) test_stack_error_detected, 1); + *guard_word_ptr = TX_STACK_FILL; + + /* 5. Test stack error detection on kernel stack overflow (SP below kernel stack start). */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + test_stack_error_detected = 0; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_start) - 4); + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("kernel stack overflow detected", (ULONG) test_stack_error_detected, 1); + + /* 6. Test stack error detection on user stack guard pattern corruption. */ + thread_ptr -> tx_thread_module_current_user_mode = thread_ptr -> tx_thread_module_user_mode; + thread_ptr -> tx_thread_stack_start = saved_user_stack_start; + thread_ptr -> tx_thread_stack_end = saved_user_stack_end; + thread_ptr -> tx_thread_stack_ptr = saved_user_stack_highest_ptr; + + test_stack_error_detected = 0; + *user_guard_ptr = 0xDEADBEEF; + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("corrupted user guard detected", (ULONG) test_stack_error_detected, 1); + *user_guard_ptr = TX_STACK_FILL; + + /* 7. Test stack error detection on user stack overflow. */ + thread_ptr -> tx_thread_module_current_user_mode = thread_ptr -> tx_thread_module_user_mode; + test_stack_error_detected = 0; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) saved_user_stack_start) - 4); + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("user stack overflow detected", (ULONG) test_stack_error_detected, 1); + + /* 8. Test _tx_thread_stack_analyze on user stack. */ + thread_ptr -> tx_thread_module_current_user_mode = thread_ptr -> tx_thread_module_user_mode; + thread_ptr -> tx_thread_stack_start = saved_user_stack_start; + thread_ptr -> tx_thread_stack_end = saved_user_stack_end; + thread_ptr -> tx_thread_stack_ptr = saved_user_stack_highest_ptr; + thread_ptr -> tx_thread_stack_highest_ptr = saved_user_stack_highest_ptr; + _tx_thread_stack_analyze(thread_ptr); + test_expect("user stack analyze preserves highest ptr validity", + (ULONG) (thread_ptr -> tx_thread_stack_highest_ptr >= thread_ptr -> tx_thread_stack_start), (ULONG) TX_TRUE); + + /* 9. Test _tx_thread_stack_analyze on kernel stack. */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + thread_ptr -> tx_thread_stack_start = thread_ptr -> tx_thread_module_kernel_stack_start; + thread_ptr -> tx_thread_stack_end = thread_ptr -> tx_thread_module_kernel_stack_end; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end) - 128); + thread_ptr -> tx_thread_module_kernel_stack_highest_ptr = thread_ptr -> tx_thread_stack_ptr; + _tx_thread_stack_analyze(thread_ptr); + test_expect("kernel stack analyze preserves highest ptr validity", + (ULONG) (thread_ptr -> tx_thread_module_kernel_stack_highest_ptr >= thread_ptr -> tx_thread_module_kernel_stack_start), (ULONG) TX_TRUE); + + /* 10. Test kernel stack check under TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE scenario. + When stack maintenance is disabled, stack_start is not updated by assembly scheduler, + so stack_start remains pointing to user stack start while current_user_mode is 0. */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + thread_ptr -> tx_thread_stack_start = saved_user_stack_start; + thread_ptr -> tx_thread_stack_end = saved_user_stack_end; + thread_ptr -> tx_thread_stack_ptr = (VOID *) (((ALIGN_TYPE) thread_ptr -> tx_thread_module_kernel_stack_end) - 64); + test_stack_error_detected = 0; + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("kernel stack check succeeds when stack maintenance is disabled", (ULONG) test_stack_error_detected, 0); + + /* 11. Test boundary condition where highest_ptr equals stack_start (under-read guard). */ + thread_ptr -> tx_thread_module_current_user_mode = 0; + thread_ptr -> tx_thread_stack_start = thread_ptr -> tx_thread_module_kernel_stack_start; + thread_ptr -> tx_thread_stack_end = thread_ptr -> tx_thread_module_kernel_stack_end; + thread_ptr -> tx_thread_stack_ptr = thread_ptr -> tx_thread_module_kernel_stack_start; + thread_ptr -> tx_thread_module_kernel_stack_highest_ptr = thread_ptr -> tx_thread_module_kernel_stack_start; + test_stack_error_detected = 0; + TX_THREAD_STACK_CHECK(thread_ptr); + test_expect("boundary condition highest_ptr == stack_start handled safely", (ULONG) test_stack_error_detected, 0); + + if (test_failures == 0U) + { + printf("SUCCESS! %lu expectations\n", (unsigned long) test_checks); + return(0); + } + + printf("ERROR: %u expectation(s) failed of %lu\n", test_failures, (unsigned long) test_checks); + return(1); +}