diff --git a/.github/workflows/freertos-mem-track.yml b/.github/workflows/freertos-mem-track.yml new file mode 100644 index 0000000000..8870b903c1 --- /dev/null +++ b/.github/workflows/freertos-mem-track.yml @@ -0,0 +1,35 @@ +name: FreeRTOS mem_track.h compile regression + +# START OF COMMON SECTION +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +# END OF COMMON SECTION + +# Regression test for the memLock / pthread.h guard misalignment in +# wolfssl/wolfcrypt/mem_track.h that previously broke multi-threaded +# FreeRTOS-class builds with WOLFSSL_TRACK_MEMORY + USE_WOLFSSL_MEMORY +# + !WOLFSSL_STATIC_MEMORY. The bug was preprocessor-only, so the test +# simulates a non-Linux/Mac/Zephyr target by suppressing the host +# platform autodefines (-U__linux__ -U__MACH__ -U__ZEPHYR__) and using +# the clean-room FreeRTOS.h / semphr.h stubs under +# tests/freertos-mem-track-repro/. No cross compiler needed. + +jobs: + freertos_mem_track: + name: mem_track.h non-Linux multi-threaded compile + if: github.repository_owner == 'wolfssl' + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - name: Checkout wolfSSL + uses: actions/checkout@v4 + + - name: Run mem_track.h FreeRTOS reproducer + run: sh tests/freertos-mem-track-repro/run.sh diff --git a/tests/freertos-mem-track-repro/FreeRTOS.h b/tests/freertos-mem-track-repro/FreeRTOS.h new file mode 100644 index 0000000000..dca2345f9e --- /dev/null +++ b/tests/freertos-mem-track-repro/FreeRTOS.h @@ -0,0 +1,38 @@ +/* FreeRTOS.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Stub FreeRTOS.h for the mem_track.h reproducer. + * + * Provides the minimum typedefs the wolfSSL header chain needs while + * pretending to be a non-Linux multi-threaded FreeRTOS target. */ +#ifndef WOLFSSL_REPRO_FREERTOS_H +#define WOLFSSL_REPRO_FREERTOS_H + +typedef void *SemaphoreHandle_t; +typedef void *xSemaphoreHandle; +typedef void *TaskHandle_t; + +/* mem_track.h calls these directly under #ifdef FREERTOS. Declarations + * only; the test compiles -c and never links. */ +extern void *pvPortMalloc(unsigned long size); +extern void vPortFree(void *ptr); + +#endif diff --git a/tests/freertos-mem-track-repro/repro.c b/tests/freertos-mem-track-repro/repro.c new file mode 100644 index 0000000000..ce0d685370 --- /dev/null +++ b/tests/freertos-mem-track-repro/repro.c @@ -0,0 +1,30 @@ +/* repro.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Reproduces the memLock / pthread.h compile failure reported by the + * FreeRTOS/Xtensa customer in wolfSSL 5.9.1. The bug is preprocessor-only + * so it triggers on any host once __linux__/__MACH__/__ZEPHYR__ are + * suppressed via -U flags. WOLFSSL_USER_SETTINGS is supplied by run.sh. */ +#include "user_settings.h" +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/mem_track.h" + +int main(void) { return 0; } diff --git a/tests/freertos-mem-track-repro/run.sh b/tests/freertos-mem-track-repro/run.sh new file mode 100755 index 0000000000..61d877e369 --- /dev/null +++ b/tests/freertos-mem-track-repro/run.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# run.sh +# +# Copyright (C) 2006-2026 wolfSSL Inc. +# +# This file is part of wolfSSL. +# +# wolfSSL is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# wolfSSL is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + +# Regression test for the mem_track.h memLock guard misalignment that +# previously broke multi-threaded FreeRTOS-class builds with +# WOLFSSL_TRACK_MEMORY + USE_WOLFSSL_MEMORY + !WOLFSSL_STATIC_MEMORY. +# +# The bug is preprocessor-only, so the test runs on a stock Linux host: +# -U__linux__ -U__MACH__ -U__ZEPHYR__ suppresses the host autodefines +# that would otherwise hide it. Stub FreeRTOS.h / semphr.h satisfy the +# wc_port.h FREERTOS_TCP mutex typedef without needing a real RTOS. +# +# Exit 0 = mem_track.h still compiles in the non-Linux multi-threaded +# config, i.e. the fix is in place. Exit non-zero = the misalignment is +# back. Run from the wolfssl repo root. +set -u +cc -DWOLFSSL_USER_SETTINGS \ + -U__linux__ -U__MACH__ -U__ZEPHYR__ \ + -I tests/freertos-mem-track-repro \ + -I . \ + -c tests/freertos-mem-track-repro/repro.c \ + -o /dev/null +status=$? +if [ $status -eq 0 ]; then + echo "OK: mem_track.h compiles for non-Linux multi-threaded config." +else + echo "FAIL: mem_track.h compile broken for non-Linux multi-threaded" >&2 + echo " config - the memLock guard misalignment may be back." >&2 +fi +exit $status diff --git a/tests/freertos-mem-track-repro/semphr.h b/tests/freertos-mem-track-repro/semphr.h new file mode 100644 index 0000000000..8526b1a678 --- /dev/null +++ b/tests/freertos-mem-track-repro/semphr.h @@ -0,0 +1,26 @@ +/* semphr.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Stub semphr.h for the mem_track.h reproducer. SemaphoreHandle_t is + * declared in the stub FreeRTOS.h. */ +#ifndef WOLFSSL_REPRO_SEMPHR_H +#define WOLFSSL_REPRO_SEMPHR_H +#endif diff --git a/tests/freertos-mem-track-repro/task.h b/tests/freertos-mem-track-repro/task.h new file mode 100644 index 0000000000..78862f96d2 --- /dev/null +++ b/tests/freertos-mem-track-repro/task.h @@ -0,0 +1,27 @@ +/* task.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Stub task.h for the mem_track.h reproducer. The wolfSSL header chain + * includes this when FREERTOS is defined; we don't need any of the real + * task API to reach the mem_track.h code under test. */ +#ifndef WOLFSSL_REPRO_TASK_H +#define WOLFSSL_REPRO_TASK_H +#endif diff --git a/tests/freertos-mem-track-repro/user_settings.h b/tests/freertos-mem-track-repro/user_settings.h new file mode 100644 index 0000000000..a864f622cd --- /dev/null +++ b/tests/freertos-mem-track-repro/user_settings.h @@ -0,0 +1,38 @@ +/* user_settings.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Minimal user_settings.h reproducing the bug state. */ +#ifndef WOLFSSL_FREERTOS_MEM_TRACK_REPRO_USER_SETTINGS_H +#define WOLFSSL_FREERTOS_MEM_TRACK_REPRO_USER_SETTINGS_H + +#define FREERTOS +#define FREERTOS_TCP +#define NO_FILESYSTEM +#define WOLFSSL_NO_SOCK +#define USE_WOLFSSL_MEMORY +#define WOLFSSL_TRACK_MEMORY +#define WOLFSSL_DEBUG_MEMORY +#define WOLFSSL_DEBUG_MEMORY_PRINT +#define DEBUG_WOLFSSL + +/* Intentionally NOT defined: SINGLE_THREADED, WOLFSSL_STATIC_MEMORY */ + +#endif diff --git a/tests/include.am b/tests/include.am index 8dcdf3a0c6..b512f72449 100644 --- a/tests/include.am +++ b/tests/include.am @@ -88,5 +88,11 @@ EXTRA_DIST += tests/unit.h \ tests/emnet/IP/IP.h \ tests/emnet/emnet_shim.c \ tests/emnet/emnet_nonblock_test.c \ - tests/emnet/Makefile + tests/emnet/Makefile \ + tests/freertos-mem-track-repro/user_settings.h \ + tests/freertos-mem-track-repro/repro.c \ + tests/freertos-mem-track-repro/FreeRTOS.h \ + tests/freertos-mem-track-repro/semphr.h \ + tests/freertos-mem-track-repro/task.h \ + tests/freertos-mem-track-repro/run.sh DISTCLEANFILES+= tests/.libs/unit.test diff --git a/wolfssl/wolfcrypt/mem_track.h b/wolfssl/wolfcrypt/mem_track.h index 7046731fb2..daef2753df 100644 --- a/wolfssl/wolfcrypt/mem_track.h +++ b/wolfssl/wolfcrypt/mem_track.h @@ -142,9 +142,12 @@ static memoryStats ourMemStats; WOLFSSL_API extern memoryStats *wc_MemStats_Ptr; #ifdef DO_MEM_LIST - #include static memoryList ourMemList; - static pthread_mutex_t memLock = PTHREAD_MUTEX_INITIALIZER; +#endif + +#if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + static wolfSSL_Mutex memLock; + static int memLockInit = 0; #endif #ifdef WOLFSSL_DEBUG_MEMORY @@ -182,7 +185,7 @@ static WC_INLINE void* TrackMalloc(size_t sz) #endif #endif #if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) - if (pthread_mutex_lock(&memLock) == 0) + if (wc_LockMutex(&memLock) == 0) { #endif @@ -228,9 +231,9 @@ static WC_INLINE void* TrackMalloc(size_t sz) ourMemList.count++; #endif #if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) - pthread_mutex_unlock(&memLock); + wc_UnLockMutex(&memLock); } -#endif /* DO_MEM_LIST */ +#endif /* !SINGLE_THREADED && (DO_MEM_LIST || DO_MEM_STATS) */ return header->thisMemory; } @@ -255,7 +258,7 @@ static WC_INLINE void TrackFree(void* ptr) sz = header->thisSize; #if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) - if (pthread_mutex_lock(&memLock) == 0) + if (wc_LockMutex(&memLock) == 0) { #endif @@ -289,7 +292,7 @@ static WC_INLINE void TrackFree(void* ptr) #endif #if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) - pthread_mutex_unlock(&memLock); + wc_UnLockMutex(&memLock); } #endif @@ -362,14 +365,27 @@ static WC_INLINE int InitMemoryTracker(void) if (ret < 0) { wc_mem_printf("wolfSSL GetAllocators failed to get the defaults\n"); } + +#if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + /* Init the mutex before installing the tracking allocators, so the + * mutex is ready as soon as another thread can enter TrackMalloc. */ + if (!memLockInit) { + if (wc_InitMutex(&memLock) != 0) { + wc_mem_printf("wc_InitMutex failed for track memory\n"); + return -1; + } + memLockInit = 1; + } +#endif + ret = wolfSSL_SetAllocators(TrackMalloc, TrackFree, TrackRealloc); if (ret < 0) { wc_mem_printf("wolfSSL SetAllocators failed for track memory\n"); return ret; } -#ifdef DO_MEM_LIST - if (pthread_mutex_lock(&memLock) == 0) +#if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + if (wc_LockMutex(&memLock) == 0) #endif { #ifdef DO_MEM_STATS @@ -387,8 +403,11 @@ static WC_INLINE int InitMemoryTracker(void) #ifdef DO_MEM_LIST XMEMSET(&ourMemList, 0, sizeof(ourMemList)); ourMemStats.memList = &ourMemList; + #endif - pthread_mutex_unlock(&memLock); + #if !defined(SINGLE_THREADED) && \ + (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + wc_UnLockMutex(&memLock); #endif } @@ -399,8 +418,8 @@ static WC_INLINE int InitMemoryTracker(void) static WC_INLINE void ShowMemoryTracker(void) { -#ifdef DO_MEM_LIST - if (pthread_mutex_lock(&memLock) == 0) +#if !defined(SINGLE_THREADED) && (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + if (wc_LockMutex(&memLock) == 0) #endif { #ifdef DO_MEM_STATS @@ -429,8 +448,11 @@ static WC_INLINE void ShowMemoryTracker(void) #endif } } + #endif - pthread_mutex_unlock(&memLock); + #if !defined(SINGLE_THREADED) && \ + (defined(DO_MEM_LIST) || defined(DO_MEM_STATS)) + wc_UnLockMutex(&memLock); #endif } } @@ -438,7 +460,13 @@ static WC_INLINE void ShowMemoryTracker(void) static WC_INLINE int CleanupMemoryTracker(void) { wc_MemStats_Ptr = NULL; - /* restore default allocators */ + /* Restore default allocators. memLock is intentionally left + * initialized for process lifetime (matching the prior static + * PTHREAD_MUTEX_INITIALIZER behavior): SetAllocators stops new + * entries into TrackMalloc/TrackFree but does not synchronize + * with in-flight calls, so freeing the mutex here would be a + * use-after-free hazard. The memLockInit flag keeps re-Init + * idempotent across an Init/Cleanup/Init cycle. */ return wolfSSL_SetAllocators(mfDefault, ffDefault, rfDefault); } #endif /* WOLFSSL_TRACK_MEMORY && USE_WOLFSSL_MEMORY && \