mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Fixes #723 With `TX_ENABLE_STACK_CHECKING` and `TX_ENABLE_RANDOM_NUMBER_STACK_FILLING` both enabled, `_tx_thread_create` picked a random byte, stored it in `tx_thread_stack_fill_value`, filled the stack with it -- and then cleared the whole control block with `TX_MEMSET`. The stack held the pattern while the control block claimed zero, so `TX_THREAD_STACK_CHECK` and `_tx_thread_stack_analyze` compared against the wrong value for the entire life of the thread. The value is now computed into a local and written back after the clear. The clear stays where it is, because the module manager's error checking walks the created list before the control block may be touched. Fixed in `common/src/tx_thread_create.c`, `common_smp/src/tx_thread_create.c` and `txm_module_manager_thread_create.c`, where it additionally left a user-mode module thread's kernel stack filled with zeros. `threadx_thread_stack_fill_value_test` creates sixteen unstarted threads and checks each control block against the pattern in its stack, tolerating a random zero byte without letting that hide the defect. Added to both suites: `ERROR #3` on `dev` in `stack_checking_rand_fill_build`, green with the fix. Five tx configurations at 104 tests, five SMP at 117, and all three sources clean under `-Wall -Wextra` across every combination of the four stack-filling switches. Assisted-by: Copilot (Opus 5) <noreply@github.com>
392 lines
16 KiB
C
392 lines
16 KiB
C
/***************************************************************************
|
|
* Copyright (c) 2024 Microsoft Corporation
|
|
* Copyright (c) 2026-present 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
|
|
**************************************************************************/
|
|
|
|
// Some portions generated by Copilot (Opus 5).
|
|
|
|
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
/** */
|
|
/** ThreadX Component */
|
|
/** */
|
|
/** Thread */
|
|
/** */
|
|
/**************************************************************************/
|
|
/**************************************************************************/
|
|
|
|
#define TX_SOURCE_CODE
|
|
|
|
|
|
/* Include necessary system files. */
|
|
|
|
#include "tx_api.h"
|
|
#include "tx_trace.h"
|
|
#include "tx_thread.h"
|
|
#include "tx_initialize.h"
|
|
|
|
|
|
/**************************************************************************/
|
|
/* */
|
|
/* FUNCTION RELEASE */
|
|
/* */
|
|
/* _tx_thread_create PORTABLE C */
|
|
/* 6.3.0 */
|
|
/* AUTHOR */
|
|
/* */
|
|
/* William E. Lamie, Microsoft Corporation */
|
|
/* */
|
|
/* DESCRIPTION */
|
|
/* */
|
|
/* This function creates a thread and places it on the list of created */
|
|
/* threads. */
|
|
/* */
|
|
/* INPUT */
|
|
/* */
|
|
/* thread_ptr Thread control block pointer */
|
|
/* name Pointer to thread name string */
|
|
/* entry_function Entry function of the thread */
|
|
/* entry_input 32-bit input value to thread */
|
|
/* stack_start Pointer to start of stack */
|
|
/* stack_size Stack size in bytes */
|
|
/* priority Priority of thread */
|
|
/* (default 0-31) */
|
|
/* preempt_threshold Preemption threshold */
|
|
/* time_slice Thread time-slice value */
|
|
/* auto_start Automatic start selection */
|
|
/* */
|
|
/* OUTPUT */
|
|
/* */
|
|
/* return status Thread create return status */
|
|
/* */
|
|
/* CALLS */
|
|
/* */
|
|
/* _tx_thread_stack_build Build initial thread stack */
|
|
/* _tx_thread_system_resume Resume automatic start thread */
|
|
/* _tx_thread_system_ni_resume Noninterruptable resume thread*/
|
|
/* */
|
|
/* CALLED BY */
|
|
/* */
|
|
/* Application Code */
|
|
/* _tx_timer_initialize Create system timer thread */
|
|
/* */
|
|
/**************************************************************************/
|
|
UINT _tx_thread_create(TX_THREAD *thread_ptr, 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)
|
|
{
|
|
|
|
TX_INTERRUPT_SAVE_AREA
|
|
|
|
TX_THREAD *next_thread;
|
|
TX_THREAD *previous_thread;
|
|
TX_THREAD *saved_thread_ptr;
|
|
UINT saved_threshold = ((UINT) 0);
|
|
UCHAR *temp_ptr;
|
|
|
|
#ifdef TX_ENABLE_STACK_CHECKING
|
|
ALIGN_TYPE new_stack_start;
|
|
ALIGN_TYPE updated_stack_start;
|
|
#endif
|
|
#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING)
|
|
ULONG stack_fill_value;
|
|
#endif
|
|
|
|
#ifndef TX_DISABLE_STACK_FILLING
|
|
#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING)
|
|
|
|
/* Initialize the stack fill value to a 8-bit random value. */
|
|
stack_fill_value = ((ULONG) TX_RAND()) & 0xFFUL;
|
|
|
|
/* Duplicate the random value in each of the 4 bytes of the stack fill value. */
|
|
stack_fill_value = stack_fill_value |
|
|
(stack_fill_value << 8) |
|
|
(stack_fill_value << 16) |
|
|
(stack_fill_value << 24);
|
|
|
|
/* Store the fill value in the control block so that the stack fill below picks it up
|
|
through the TX_STACK_FILL macro. The control block is cleared further down in this
|
|
function, so the value is stored again once that has been done. */
|
|
thread_ptr -> tx_thread_stack_fill_value = stack_fill_value;
|
|
#endif
|
|
|
|
/* Set the thread stack to a pattern prior to creating the initial
|
|
stack frame. This pattern is used by the stack checking routines
|
|
to see how much has been used. */
|
|
TX_MEMSET(stack_start, ((UCHAR) TX_STACK_FILL), stack_size);
|
|
#endif
|
|
|
|
#ifdef TX_ENABLE_STACK_CHECKING
|
|
|
|
/* Ensure that there are two ULONG of 0xEF patterns at the top and
|
|
bottom of the thread's stack. This will be used to check for stack
|
|
overflow conditions during run-time. */
|
|
stack_size = ((stack_size/(sizeof(ULONG))) * (sizeof(ULONG))) - (sizeof(ULONG));
|
|
|
|
/* Ensure the starting stack address is evenly aligned. */
|
|
#ifdef TX_MISRA_ENABLE
|
|
new_stack_start = TX_POINTER_TO_ULONG_CONVERT(stack_start);
|
|
#else
|
|
new_stack_start = TX_POINTER_TO_ALIGN_TYPE_CONVERT(stack_start);
|
|
#endif /* TX_MISRA_ENABLE */
|
|
updated_stack_start = (((new_stack_start) + ((sizeof(ULONG)) - ((ULONG) 1)) ) & (~((sizeof(ULONG)) - ((ULONG) 1))));
|
|
|
|
/* Determine if the starting stack address is different. */
|
|
if (new_stack_start != updated_stack_start)
|
|
{
|
|
|
|
/* Yes, subtract another ULONG from the size to avoid going past the stack area. */
|
|
stack_size = stack_size - (sizeof(ULONG));
|
|
}
|
|
|
|
/* Update the starting stack pointer. */
|
|
#ifdef TX_MISRA_ENABLE
|
|
stack_start = TX_ULONG_TO_POINTER_CONVERT(updated_stack_start);
|
|
#else
|
|
stack_start = TX_ALIGN_TYPE_TO_POINTER_CONVERT(updated_stack_start);
|
|
#endif /* TX_MISRA_ENABLE */
|
|
#endif
|
|
|
|
/* Prepare the thread control block prior to placing it on the created
|
|
list. */
|
|
|
|
/* Initialize thread control block to all zeros. */
|
|
TX_MEMSET(thread_ptr, 0, (sizeof(TX_THREAD)));
|
|
|
|
#if defined(TX_ENABLE_RANDOM_NUMBER_STACK_FILLING) && defined(TX_ENABLE_STACK_CHECKING) && !defined(TX_DISABLE_STACK_FILLING)
|
|
|
|
/* Clearing the control block reset the stack fill value, so store the value that was
|
|
actually used to fill the stack again. Otherwise the stack checking and stack analyze
|
|
routines would look for a pattern that is not the one present in the stack. */
|
|
thread_ptr -> tx_thread_stack_fill_value = stack_fill_value;
|
|
#endif
|
|
|
|
/* Place the supplied parameters into the thread's control block. */
|
|
thread_ptr -> tx_thread_name = name_ptr;
|
|
thread_ptr -> tx_thread_entry = entry_function;
|
|
thread_ptr -> tx_thread_entry_parameter = entry_input;
|
|
thread_ptr -> tx_thread_stack_start = stack_start;
|
|
thread_ptr -> tx_thread_stack_size = stack_size;
|
|
thread_ptr -> tx_thread_priority = priority;
|
|
thread_ptr -> tx_thread_user_priority = priority;
|
|
thread_ptr -> tx_thread_time_slice = time_slice;
|
|
thread_ptr -> tx_thread_new_time_slice = time_slice;
|
|
thread_ptr -> tx_thread_inherit_priority = ((UINT) TX_MAX_PRIORITIES);
|
|
|
|
/* Calculate the end of the thread's stack area. */
|
|
temp_ptr = TX_VOID_TO_UCHAR_POINTER_CONVERT(stack_start);
|
|
temp_ptr = (TX_UCHAR_POINTER_ADD(temp_ptr, (stack_size - ((ULONG) 1))));
|
|
thread_ptr -> tx_thread_stack_end = TX_UCHAR_TO_VOID_POINTER_CONVERT(temp_ptr);
|
|
|
|
#ifndef TX_DISABLE_PREEMPTION_THRESHOLD
|
|
|
|
/* Preemption-threshold is enabled, setup accordingly. */
|
|
thread_ptr -> tx_thread_preempt_threshold = preempt_threshold;
|
|
thread_ptr -> tx_thread_user_preempt_threshold = preempt_threshold;
|
|
#else
|
|
|
|
/* Preemption-threshold is disabled, determine if preemption-threshold was required. */
|
|
if (priority != preempt_threshold)
|
|
{
|
|
|
|
/* Preemption-threshold specified. Since specific preemption-threshold is not supported,
|
|
disable all preemption. */
|
|
thread_ptr -> tx_thread_preempt_threshold = ((UINT) 0);
|
|
thread_ptr -> tx_thread_user_preempt_threshold = ((UINT) 0);
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Preemption-threshold is not specified, just setup with the priority. */
|
|
thread_ptr -> tx_thread_preempt_threshold = priority;
|
|
thread_ptr -> tx_thread_user_preempt_threshold = priority;
|
|
}
|
|
#endif
|
|
|
|
/* Now fill in the values that are required for thread initialization. */
|
|
thread_ptr -> tx_thread_state = TX_SUSPENDED;
|
|
|
|
/* Setup the necessary fields in the thread timer block. */
|
|
TX_THREAD_CREATE_TIMEOUT_SETUP(thread_ptr)
|
|
|
|
/* Perform any additional thread setup activities for tool or user purpose. */
|
|
TX_THREAD_CREATE_INTERNAL_EXTENSION(thread_ptr)
|
|
|
|
/* Call the target specific stack frame building routine to build the
|
|
thread's initial stack and to setup the actual stack pointer in the
|
|
control block. */
|
|
_tx_thread_stack_build(thread_ptr, _tx_thread_shell_entry);
|
|
|
|
#ifdef TX_ENABLE_STACK_CHECKING
|
|
|
|
/* Setup the highest usage stack pointer. */
|
|
thread_ptr -> tx_thread_stack_highest_ptr = thread_ptr -> tx_thread_stack_ptr;
|
|
#endif
|
|
|
|
/* Prepare to make this thread a member of the created thread list. */
|
|
TX_DISABLE
|
|
|
|
/* Load the thread ID field in the thread control block. */
|
|
thread_ptr -> tx_thread_id = TX_THREAD_ID;
|
|
|
|
/* Place the thread on the list of created threads. First,
|
|
check for an empty list. */
|
|
if (_tx_thread_created_count == TX_EMPTY)
|
|
{
|
|
|
|
/* The created thread list is empty. Add thread to empty list. */
|
|
_tx_thread_created_ptr = thread_ptr;
|
|
thread_ptr -> tx_thread_created_next = thread_ptr;
|
|
thread_ptr -> tx_thread_created_previous = thread_ptr;
|
|
}
|
|
else
|
|
{
|
|
|
|
/* This list is not NULL, add to the end of the list. */
|
|
next_thread = _tx_thread_created_ptr;
|
|
previous_thread = next_thread -> tx_thread_created_previous;
|
|
|
|
/* Place the new thread in the list. */
|
|
next_thread -> tx_thread_created_previous = thread_ptr;
|
|
previous_thread -> tx_thread_created_next = thread_ptr;
|
|
|
|
/* Setup this thread's created links. */
|
|
thread_ptr -> tx_thread_created_previous = previous_thread;
|
|
thread_ptr -> tx_thread_created_next = next_thread;
|
|
}
|
|
|
|
/* Increment the thread created count. */
|
|
_tx_thread_created_count++;
|
|
|
|
/* If trace is enabled, register this object. */
|
|
TX_TRACE_OBJECT_REGISTER(TX_TRACE_OBJECT_TYPE_THREAD, thread_ptr, name_ptr, TX_POINTER_TO_ULONG_CONVERT(stack_start), stack_size)
|
|
|
|
/* If trace is enabled, insert this event into the trace buffer. */
|
|
TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_CREATE, thread_ptr, priority, TX_POINTER_TO_ULONG_CONVERT(stack_start), stack_size, TX_TRACE_THREAD_EVENTS)
|
|
|
|
/* Register thread in the thread array structure. */
|
|
TX_EL_THREAD_REGISTER(thread_ptr)
|
|
|
|
/* Log this kernel call. */
|
|
TX_EL_THREAD_CREATE_INSERT
|
|
|
|
#ifndef TX_NOT_INTERRUPTABLE
|
|
|
|
/* Temporarily disable preemption. */
|
|
_tx_thread_preempt_disable++;
|
|
#endif
|
|
|
|
/* Determine if an automatic start was requested. If so, call the resume
|
|
thread function and then check for a preemption condition. */
|
|
if (auto_start == TX_AUTO_START)
|
|
{
|
|
|
|
/* Determine if the create call is being called from initialization. */
|
|
if (TX_THREAD_GET_SYSTEM_STATE() >= TX_INITIALIZE_IN_PROGRESS)
|
|
{
|
|
|
|
/* Yes, this create call was made from initialization. */
|
|
|
|
/* Pickup the current thread execute pointer, which corresponds to the
|
|
highest priority thread ready to execute. Interrupt lockout is
|
|
not required, since interrupts are assumed to be disabled during
|
|
initialization. */
|
|
saved_thread_ptr = _tx_thread_execute_ptr;
|
|
|
|
/* Determine if there is thread ready for execution. */
|
|
if (saved_thread_ptr != TX_NULL)
|
|
{
|
|
|
|
/* Yes, a thread is ready for execution when initialization completes. */
|
|
|
|
/* Save the current preemption-threshold. */
|
|
saved_threshold = saved_thread_ptr -> tx_thread_preempt_threshold;
|
|
|
|
/* For initialization, temporarily set the preemption-threshold to the
|
|
priority level to make sure the highest-priority thread runs once
|
|
initialization is complete. */
|
|
saved_thread_ptr -> tx_thread_preempt_threshold = saved_thread_ptr -> tx_thread_priority;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/* Simply set the saved thread pointer to NULL. */
|
|
saved_thread_ptr = TX_NULL;
|
|
}
|
|
|
|
#ifdef TX_NOT_INTERRUPTABLE
|
|
|
|
/* Perform any additional activities for tool or user purpose. */
|
|
TX_THREAD_CREATE_EXTENSION(thread_ptr)
|
|
|
|
/* Resume the thread! */
|
|
_tx_thread_system_ni_resume(thread_ptr);
|
|
|
|
/* Restore previous interrupt posture. */
|
|
TX_RESTORE
|
|
#else
|
|
|
|
/* Restore previous interrupt posture. */
|
|
TX_RESTORE
|
|
|
|
/* Perform any additional activities for tool or user purpose. */
|
|
TX_THREAD_CREATE_EXTENSION(thread_ptr)
|
|
|
|
/* Call the resume thread function to make this thread ready. */
|
|
_tx_thread_system_resume(thread_ptr);
|
|
#endif
|
|
|
|
/* Determine if the thread's preemption-threshold needs to be restored. */
|
|
if (saved_thread_ptr != TX_NULL)
|
|
{
|
|
|
|
/* Yes, restore the previous highest-priority thread's preemption-threshold. This
|
|
can only happen if this routine is called from initialization. */
|
|
saved_thread_ptr -> tx_thread_preempt_threshold = saved_threshold;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
#ifdef TX_NOT_INTERRUPTABLE
|
|
|
|
/* Perform any additional activities for tool or user purpose. */
|
|
TX_THREAD_CREATE_EXTENSION(thread_ptr)
|
|
|
|
/* Restore interrupts. */
|
|
TX_RESTORE
|
|
#else
|
|
|
|
/* Restore interrupts. */
|
|
TX_RESTORE
|
|
|
|
/* Perform any additional activities for tool or user purpose. */
|
|
TX_THREAD_CREATE_EXTENSION(thread_ptr)
|
|
|
|
/* Disable interrupts. */
|
|
TX_DISABLE
|
|
|
|
/* Re-enable preemption. */
|
|
_tx_thread_preempt_disable--;
|
|
|
|
/* Restore interrupts. */
|
|
TX_RESTORE
|
|
|
|
/* Check for preemption. */
|
|
_tx_thread_system_preempt_check();
|
|
#endif
|
|
}
|
|
|
|
/* Always return a success. */
|
|
return(TX_SUCCESS);
|
|
}
|
|
|