From f04aca7f9322ca098f513d58af78715552db5cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 10 Sep 2026 14:28:26 -0400 Subject: [PATCH] Guarded the stack analyze binary search against a highest stack pointer that is not above the start of the stack, so a stack overflow or a corrupted control block no longer hangs or crashes the caller _tx_thread_stack_analyze computed the midpoint of the remaining stack with TX_ULONG_POINTER_DIF, which casts the pointer difference to ULONG. When tx_thread_stack_highest_ptr had already moved below tx_thread_stack_start, the difference was negative and wrapped to a huge unsigned value, so the binary search probed far outside the stack and never converged. The highest stack pointer is now required to be above the start of the stack before the search begins, and the final scan for the first used word is bounded by the initial highest stack pointer so it cannot run past the end of the region either. Added regression coverage for an inverted and for an equal pair of stack pointers. Without the fix the new test case crashes the suite. Assisted-by: Copilot (Opus 5) --- common/src/tx_thread_stack_analyze.c | 13 ++++++++++--- common_smp/src/tx_thread_stack_analyze.c | 13 ++++++++++--- .../threadx_thread_stack_checking_test.c | 15 +++++++++++++++ .../threadx_thread_stack_checking_test.c | 15 +++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/common/src/tx_thread_stack_analyze.c b/common/src/tx_thread_stack_analyze.c index e3023f13d..37a5ac2d0 100644 --- a/common/src/tx_thread_stack_analyze.c +++ b/common/src/tx_thread_stack_analyze.c @@ -73,6 +73,7 @@ ULONG *stack_ptr; ULONG *stack_lowest; ULONG *stack_highest; ULONG *probe_ptr; +ULONG *stack_limit; ULONG probe_count; UINT fill_present; ULONG size; @@ -99,10 +100,16 @@ ULONG size; /* Pickup the highest stack pointer. */ stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); - /* Determine if the pointer is null. */ - if (stack_highest != TX_NULL) + /* 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 + control block, and the unsigned pointer arithmetic in the binary search below would + wrap around and never converge, hanging the caller. */ + if ((stack_highest != TX_NULL) && (stack_highest > stack_lowest)) { + /* Remember the upper bound of the search so the scan below cannot run past it. */ + stack_limit = stack_highest; + /* Restore interrupts. */ TX_RESTORE @@ -173,7 +180,7 @@ ULONG size; } while(size > ((ULONG) 1)); /* Position to first used word - at this point we are within a few words. */ - while (*stack_ptr == TX_STACK_FILL) + while ((stack_ptr < stack_limit) && (*stack_ptr == TX_STACK_FILL)) { /* Position to next word in stack. */ diff --git a/common_smp/src/tx_thread_stack_analyze.c b/common_smp/src/tx_thread_stack_analyze.c index e3023f13d..37a5ac2d0 100644 --- a/common_smp/src/tx_thread_stack_analyze.c +++ b/common_smp/src/tx_thread_stack_analyze.c @@ -73,6 +73,7 @@ ULONG *stack_ptr; ULONG *stack_lowest; ULONG *stack_highest; ULONG *probe_ptr; +ULONG *stack_limit; ULONG probe_count; UINT fill_present; ULONG size; @@ -99,10 +100,16 @@ ULONG size; /* Pickup the highest stack pointer. */ stack_highest = TX_VOID_TO_ULONG_POINTER_CONVERT(thread_ptr -> tx_thread_stack_highest_ptr); - /* Determine if the pointer is null. */ - if (stack_highest != TX_NULL) + /* 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 + control block, and the unsigned pointer arithmetic in the binary search below would + wrap around and never converge, hanging the caller. */ + if ((stack_highest != TX_NULL) && (stack_highest > stack_lowest)) { + /* Remember the upper bound of the search so the scan below cannot run past it. */ + stack_limit = stack_highest; + /* Restore interrupts. */ TX_RESTORE @@ -173,7 +180,7 @@ ULONG size; } while(size > ((ULONG) 1)); /* Position to first used word - at this point we are within a few words. */ - while (*stack_ptr == TX_STACK_FILL) + while ((stack_ptr < stack_limit) && (*stack_ptr == TX_STACK_FILL)) { /* Position to next word in stack. */ diff --git a/test/smp/regression/threadx_thread_stack_checking_test.c b/test/smp/regression/threadx_thread_stack_checking_test.c index 9db10d0aa..d30e624f1 100644 --- a/test/smp/regression/threadx_thread_stack_checking_test.c +++ b/test/smp/regression/threadx_thread_stack_checking_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +// Some portions generated by Copilot (Opus 5). + /* This test is for the thread stack checking services. */ #include @@ -200,6 +202,19 @@ TX_THREAD fake_thread; fake_thread.tx_thread_stack_highest_ptr = TX_NULL; _tx_thread_stack_analyze(&fake_thread); + /* Call the stack analyze with a highest stack pointer below the start of the stack, which is + what a stack overflow or a corrupted control block looks like. This used to hang. */ + fake_thread.tx_thread_id = ((ULONG) 0x54485244); + fake_thread.tx_thread_stack_start = (void *) 0x2000; + fake_thread.tx_thread_stack_highest_ptr = (void *) 0x1000; + _tx_thread_stack_analyze(&fake_thread); + + /* Call the stack analyze with a highest stack pointer equal to the start of the stack. */ + fake_thread.tx_thread_id = ((ULONG) 0x54485244); + fake_thread.tx_thread_stack_start = (void *) 0x2000; + fake_thread.tx_thread_stack_highest_ptr = (void *) 0x2000; + _tx_thread_stack_analyze(&fake_thread); + /* Clear the pattern in thread 2's stack. */ TX_MEMSET(thread_2_stack_start, (CHAR) 0x11, TEST_STACK_SIZE_PRINTF); diff --git a/test/tx/regression/threadx_thread_stack_checking_test.c b/test/tx/regression/threadx_thread_stack_checking_test.c index 9db10d0aa..d30e624f1 100644 --- a/test/tx/regression/threadx_thread_stack_checking_test.c +++ b/test/tx/regression/threadx_thread_stack_checking_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +// Some portions generated by Copilot (Opus 5). + /* This test is for the thread stack checking services. */ #include @@ -200,6 +202,19 @@ TX_THREAD fake_thread; fake_thread.tx_thread_stack_highest_ptr = TX_NULL; _tx_thread_stack_analyze(&fake_thread); + /* Call the stack analyze with a highest stack pointer below the start of the stack, which is + what a stack overflow or a corrupted control block looks like. This used to hang. */ + fake_thread.tx_thread_id = ((ULONG) 0x54485244); + fake_thread.tx_thread_stack_start = (void *) 0x2000; + fake_thread.tx_thread_stack_highest_ptr = (void *) 0x1000; + _tx_thread_stack_analyze(&fake_thread); + + /* Call the stack analyze with a highest stack pointer equal to the start of the stack. */ + fake_thread.tx_thread_id = ((ULONG) 0x54485244); + fake_thread.tx_thread_stack_start = (void *) 0x2000; + fake_thread.tx_thread_stack_highest_ptr = (void *) 0x2000; + _tx_thread_stack_analyze(&fake_thread); + /* Clear the pattern in thread 2's stack. */ TX_MEMSET(thread_2_stack_start, (CHAR) 0x11, TEST_STACK_SIZE_PRINTF);