Fix mem_track.h compile failure on multi-threaded non-Linux builds

The memLock mutex and #include <pthread.h> in mem_track.h were
declared under #ifdef DO_MEM_LIST (Linux/macOS/Zephyr only), but
referenced under the broader guard

    !defined(SINGLE_THREADED) && \
    (defined(DO_MEM_LIST) || defined(DO_MEM_STATS))

Since DO_MEM_STATS is defined whenever WOLFSSL_TRACK_MEMORY +
USE_WOLFSSL_MEMORY are set without WOLFSSL_STATIC_MEMORY, any
non-Linux/Mac/Zephyr multi-threaded build failed to compile with
implicit pthread_mutex_lock declarations and undeclared memLock.

Replace the raw pthread mutex with wolfSSL's portable mutex API
(wc_InitMutex / wc_LockMutex / wc_UnLockMutex / wc_FreeMutex) so
locking works on every platform wolfSSL already ports to.
InitMemoryTracker now calls wc_InitMutex before
wolfSSL_SetAllocators installs TrackMalloc, guarded by a
memLockInit flag for idempotency. CleanupMemoryTracker calls
wc_FreeMutex after restoring the default allocators so no
in-flight allocation races a freed mutex. The four mutex guards
in TrackMalloc/TrackFree and the two in InitMemoryTracker/
ShowMemoryTracker are unified on the same condition as the
memLock declaration itself.

ZD #21763
This commit is contained in:
Andrew Hutchings
2026-05-11 16:06:26 +01:00
parent 01ba609f0d
commit 7b89d82b35
9 changed files with 291 additions and 15 deletions
+35
View File
@@ -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
+38
View File
@@ -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
+30
View File
@@ -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; }
+48
View File
@@ -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
+26
View File
@@ -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
+27
View File
@@ -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
@@ -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
+7 -1
View File
@@ -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
+42 -14
View File
@@ -142,9 +142,12 @@ static memoryStats ourMemStats;
WOLFSSL_API extern memoryStats *wc_MemStats_Ptr;
#ifdef DO_MEM_LIST
#include <pthread.h>
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 && \