Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions test/tx/cmake/regression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ set(regression_test_cases
${SOURCE_DIR}/threadx_thread_create_preemption_threshold_test.c
${SOURCE_DIR}/threadx_thread_delayed_suspension_test.c
${SOURCE_DIR}/threadx_thread_information_test.c
${SOURCE_DIR}/threadx_thread_misaligned_stack_test.c
${SOURCE_DIR}/threadx_thread_multi_level_preemption_threshold_test.c
${SOURCE_DIR}/threadx_thread_multiple_non_current_test.c
${SOURCE_DIR}/threadx_thread_multiple_sleep_test.c
Expand Down Expand Up @@ -109,6 +110,7 @@ set(regression_test_cases
${SOURCE_DIR}/threadx_timer_multiple_test.c
${SOURCE_DIR}/threadx_timer_simple_test.c
${SOURCE_DIR}/threadx_trace_basic_test.c
${SOURCE_DIR}/threadx_trace_entry_update_test.c
${SOURCE_DIR}/threadx_initialize_kernel_setup_test.c)

add_custom_command(
Expand Down
150 changes: 150 additions & 0 deletions test/tx/regression/threadx_thread_misaligned_stack_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/***************************************************************************
* 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.
*
* 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
**************************************************************************/

/* This test creates a thread whose stack does not begin on a ULONG boundary.

tx_thread_create rounds the starting address up to the next ULONG and then
takes a ULONG off the size, so that the rounding cannot push the end of the
stack past the memory the caller supplied:

if (new_stack_start != updated_stack_start)
{
stack_size = stack_size - (sizeof(ULONG));
}

Every other test in the suite hands tx_thread_create an aligned stack, so
that subtraction was never executed -- the only uncovered line in
tx_thread_create.c across all five build configurations. A misaligned stack
is legitimate: the API takes a VOID * and the alignment fix-up exists
precisely to accept one.

The thread is then run to completion rather than merely created, because the
point of the adjustment is that the resulting stack is still usable and still
inside the caller's buffer. */

#include <stdio.h>
#include "tx_api.h"


void test_control_return(UINT status);


static TX_THREAD thread_0;
static TX_THREAD thread_1;

/* Thread 1's stack is carved out of this by hand so that its start can be put
deliberately off a ULONG boundary. The extra ULONG is what the misaligned
start is offset into, so that the stack still ends inside the array after
tx_thread_create has rounded the start up. */

static UCHAR misaligned_area[TEST_STACK_SIZE_PRINTF + sizeof(ULONG)];

static ULONG thread_1_counter = 0;


static void thread_0_entry(ULONG thread_input);
static void thread_1_entry(ULONG thread_input);


/* Define what the initial system looks like. */

#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void threadx_thread_misaligned_stack_application_define(void *first_unused_memory)
#endif
{

UINT status;
CHAR *pointer;
CHAR *misaligned_stack;


/* Setup a pointer. */
pointer = (CHAR *) first_unused_memory;

status = tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
pointer, TEST_STACK_SIZE_PRINTF,
16, 16, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + TEST_STACK_SIZE_PRINTF;

if (status != TX_SUCCESS)
{

printf("Running Thread Misaligned Stack Test................................ ERROR #1\n");
test_control_return(1);
}

/* Push the start one byte past a ULONG boundary. The array itself is
aligned, so adding one is enough to guarantee the fix-up runs whatever
the alignment of the array turned out to be. */
misaligned_stack = ((CHAR *) misaligned_area) + 1;

status = tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,
misaligned_stack, TEST_STACK_SIZE_PRINTF,
15, 15, TX_NO_TIME_SLICE, TX_AUTO_START);

if (status != TX_SUCCESS)
{

printf("Running Thread Misaligned Stack Test................................ ERROR #2\n");
test_control_return(1);
}
}


static void thread_0_entry(ULONG thread_input)
{

/* Thread 1 is the higher priority of the two, so this thread first gets the
processor while thread 1 is sleeping. Sleep past that, so the check below
is made against a thread that has actually finished. */
tx_thread_sleep(5);

/* Both increments must have happened: one before thread 1 slept and one
after it woke, which is the half that needed the stack to survive a
context switch. */
if (thread_1_counter != ((ULONG) 2))
{

printf("Running Thread Misaligned Stack Test................................ ERROR #3\n");
test_control_return(1);
}

/* The thread that ran on the misaligned stack must have run to completion,
which is what says the adjusted stack was usable. */
if (thread_1.tx_thread_state != TX_COMPLETED)
{

printf("Running Thread Misaligned Stack Test................................ ERROR #4\n");
test_control_return(1);
}

printf("Running Thread Misaligned Stack Test................................ SUCCESS!\n");
test_control_return(0);
}


static void thread_1_entry(ULONG thread_input)
{

/* Use the stack either side of a context switch rather than returning
straight away. */
thread_1_counter++;

tx_thread_sleep(1);

thread_1_counter++;
}
Loading