Skip to content
Open
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
28 changes: 22 additions & 6 deletions portable/ThirdParty/GCC/Posix/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ static void prvInitThreadKey( void );
static void prvMarkAsFreeRTOSThread( void );
static BaseType_t prvIsFreeRTOSThread( void );
static void prvDestroyThreadKey( void );
static void prvFatalError( const char * pcCall,
int iErrno ) __attribute__( ( __noreturn__ ) );
/*-----------------------------------------------------------*/

static void prvThreadKeyDestructor( void * pvData )
Expand All @@ -139,7 +141,13 @@ static void prvThreadKeyDestructor( void * pvData )

static void prvInitThreadKey( void )
{
pthread_key_create( &xThreadKey, prvThreadKeyDestructor );
int iRet = pthread_key_create( &xThreadKey, prvThreadKeyDestructor );

if( iRet != 0 )
{
prvFatalError( "pthread_key_create", iRet );
}

/* Destroy xThreadKey when the process exits. */
atexit( prvDestroyThreadKey );
}
Expand All @@ -148,15 +156,26 @@ static void prvInitThreadKey( void )
static void prvMarkAsFreeRTOSThread( void )
{
uint8_t * pucThreadData = NULL;
int iRet;

( void ) pthread_once( &hThreadKeyOnce, prvInitThreadKey );

pucThreadData = malloc( 1 );
configASSERT( pucThreadData != NULL );

if( pucThreadData == NULL )
{
prvFatalError( "malloc", ENOMEM );
}

*pucThreadData = 1;

pthread_setspecific( xThreadKey, pucThreadData );
iRet = pthread_setspecific( xThreadKey, pucThreadData );

if( iRet != 0 )
{
free( pucThreadData );
prvFatalError( "pthread_setspecific", iRet );
}
}
/*-----------------------------------------------------------*/

Expand Down Expand Up @@ -184,9 +203,6 @@ static void prvDestroyThreadKey( void )
}
/*-----------------------------------------------------------*/

static void prvFatalError( const char * pcCall,
int iErrno ) __attribute__( ( __noreturn__ ) );

void prvFatalError( const char * pcCall,
int iErrno )
{
Expand Down
33 changes: 33 additions & 0 deletions portable/ThirdParty/GCC/Posix/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.15)
project(posix_port_tests C)
enable_testing()
find_package(Threads REQUIRED)

get_filename_component(KERNEL_ROOT "../../../../.." ABSOLUTE)
add_library(freertos_config INTERFACE)
target_include_directories(freertos_config INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
set(FREERTOS_PORT GCC_POSIX CACHE STRING "" FORCE)
set(FREERTOS_HEAP 4 CACHE STRING "" FORCE)
add_subdirectory("${KERNEL_ROOT}" kernel)

foreach(assert_enabled 0 1)
set(target thread_marker_${assert_enabled})
add_executable(${target} thread_marker.c)
target_include_directories(${target} PRIVATE . .. "${KERNEL_ROOT}/include")
target_compile_definitions(${target} PRIVATE TEST_ASSERT_ENABLED=${assert_enabled})
target_compile_options(${target} PRIVATE -Wall -Wextra -Werror -ffunction-sections -fdata-sections)
target_link_libraries(${target} PRIVATE Threads::Threads)
target_link_options(${target} PRIVATE -Wl,--gc-sections
-Wl,--wrap=pthread_key_create -Wl,--wrap=pthread_setspecific
-Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=abort)
foreach(scenario success key_create key_query allocation setspecific)
add_test(NAME ${target}_${scenario} COMMAND ${target} ${scenario})
set_tests_properties(${target}_${scenario} PROPERTIES TIMEOUT 10)
endforeach()
endforeach()

add_executable(scheduler_smoke scheduler_smoke.c)
target_link_libraries(scheduler_smoke PRIVATE freertos_kernel)
target_compile_options(scheduler_smoke PRIVATE -Wall -Wextra -Werror)
add_test(NAME scheduler_smoke COMMAND scheduler_smoke)
set_tests_properties(scheduler_smoke PROPERTIES TIMEOUT 10)
28 changes: 28 additions & 0 deletions portable/ThirdParty/GCC/Posix/tests/FreeRTOSConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: MIT */
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

#include <stdlib.h>

#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configTICK_RATE_HZ 100
#define configMAX_PRIORITIES 4
#define configMINIMAL_STACK_SIZE 4096
#define configTOTAL_HEAP_SIZE ( 256 * 1024 )
#define configMAX_TASK_NAME_LEN 16
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configUSE_TIMERS 0
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1

#if TEST_ASSERT_ENABLED
#define configASSERT( x ) do { if( !( x ) ) { abort(); } } while( 0 )
#endif

#endif /* ifndef FREERTOS_CONFIG_H */
23 changes: 23 additions & 0 deletions portable/ThirdParty/GCC/Posix/tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# POSIX thread-marker regression tests

Run on Linux with GCC, CMake, and a GNU-compatible linker:

```sh
cmake -S portable/ThirdParty/GCC/Posix/tests -B build/posix-tests
cmake --build build/posix-tests
ctest --test-dir build/posix-tests --output-on-failure
```

The failure-injection executable includes the actual port implementation to
reach its private helpers. Linker wrappers inject key-creation, allocation, and
TLS-storage failures. Each failure must terminate via the fatal handler; failed
TLS storage must first free the allocated marker. Tests run with assertions
both enabled and disabled. The success case checks thread identity, isolation
from the calling thread, and automatic destructor cleanup after pthread exit.
The separate smoke test links the full kernel and runs a real scheduled task
through a tick delay and scheduler shutdown.

For sanitizer validation, configure another build directory with
`-DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer"`.
These tests are specific to the POSIX simulator; they do not validate hardware
ports. The kernel's main CMock suite lives in the parent FreeRTOS repository.
24 changes: 24 additions & 0 deletions portable/ThirdParty/GCC/Posix/tests/scheduler_smoke.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: MIT */
#include "FreeRTOS.h"
#include "task.h"

static volatile int iTaskRan;

static void prvTask( void * pvUnused )
{
( void ) pvUnused;
vTaskDelay( 2 );
iTaskRan = 1;
vTaskEndScheduler();
}

int main( void )
{
if( xTaskCreate( prvTask, "smoke", configMINIMAL_STACK_SIZE, NULL, 1, NULL ) != pdPASS )
{
return EXIT_FAILURE;
}

vTaskStartScheduler();
return iTaskRan ? EXIT_SUCCESS : EXIT_FAILURE;
}
145 changes: 145 additions & 0 deletions portable/ThirdParty/GCC/Posix/tests/thread_marker.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/* SPDX-License-Identifier: MIT */

/* Include the implementation to exercise its private TLS helpers unchanged.
* Unused scheduler sections are discarded by the linker. */
#include "../port.c"

static const char * pcScenario;
static void * pvMarker;
static int iAllocations;
static int iFrees;
static int iStores;

extern void * __real_malloc( size_t xSize );
extern void __real_free( void * pvPointer );
extern int __real_pthread_key_create( pthread_key_t * pxKey,
void ( * pxDestructor )( void * ) );
extern int __real_pthread_setspecific( pthread_key_t xKey,
const void * pvValue );

int __wrap_pthread_key_create( pthread_key_t * pxKey,
void ( * pxDestructor )( void * ) )
{
if( ( strcmp( pcScenario, "key_create" ) == 0 ) || ( strcmp( pcScenario, "key_query" ) == 0 ) )
{
return EAGAIN;
}

return __real_pthread_key_create( pxKey, pxDestructor );
}

void * __wrap_malloc( size_t xSize )
{
if( xSize == 1 )
{
iAllocations++;

if( strcmp( pcScenario, "allocation" ) == 0 )
{
return NULL;
}

pvMarker = __real_malloc( xSize );
return pvMarker;
}

return __real_malloc( xSize );
}

void __wrap_free( void * pvPointer )
{
if( ( pvPointer != NULL ) && ( pvPointer == pvMarker ) )
{
iFrees++;
}

__real_free( pvPointer );
}

int __wrap_pthread_setspecific( pthread_key_t xKey,
const void * pvValue )
{
iStores++;

if( strcmp( pcScenario, "setspecific" ) == 0 )
{
return ENOMEM;
}

return __real_pthread_setspecific( xKey, pvValue );
}

void __wrap_abort( void )
{
int iPassed = 0;

if( ( strcmp( pcScenario, "key_create" ) == 0 ) || ( strcmp( pcScenario, "key_query" ) == 0 ) )
{
iPassed = ( iAllocations == 0 ) && ( iStores == 0 );
}
else if( strcmp( pcScenario, "allocation" ) == 0 )
{
iPassed = ( iAllocations == 1 ) && ( iStores == 0 );
}
else if( strcmp( pcScenario, "setspecific" ) == 0 )
{
iPassed = ( iAllocations == 1 ) && ( iStores == 1 ) && ( iFrees == 1 );
}

_Exit( iPassed ? EXIT_SUCCESS : EXIT_FAILURE );
}

static void * prvMarkedThread( void * pvUnused )
{
( void ) pvUnused;

if( prvIsFreeRTOSThread() != pdFALSE )
{
return ( void * ) 1;
}

prvMarkAsFreeRTOSThread();
return ( void * ) ( intptr_t ) ( prvIsFreeRTOSThread() != pdTRUE );
}

int main( int argc,
char ** argv )
{
pthread_t xThread;
void * pvResult;

if( argc != 2 )
{
return EXIT_FAILURE;
}

pcScenario = argv[ 1 ];

if( strcmp( pcScenario, "success" ) == 0 )
{
if( pthread_create( &xThread, NULL, prvMarkedThread, NULL ) != 0 )
{
return EXIT_FAILURE;
}

if( pthread_join( xThread, &pvResult ) != 0 )
{
return EXIT_FAILURE;
}

return ( ( pvResult == NULL ) && ( iAllocations == 1 ) && ( iFrees == 1 ) &&
( prvIsFreeRTOSThread() == pdFALSE ) ) ? EXIT_SUCCESS : EXIT_FAILURE;
}

if( strcmp( pcScenario, "key_query" ) == 0 )
{
( void ) prvIsFreeRTOSThread();
}
else
{
prvMarkAsFreeRTOSThread();
}

fprintf( stderr, "Continued after injected %s failure\n", pcScenario );
return EXIT_FAILURE;
}