2026-04-24 11:35 AM - last edited on 2026-04-27 9:39 AM by Andrew Neil
STM32CubeMX 6.17.0
STM32CubeCLT 1.21.0 which provides GNU Tools for STM32 which provides a toolchain with GCC 14.3 and newlib 4.5.0.
STM32CubeH5 1.6.0 (as downloaded by STM32CubeMX)
STM32H563
With these tools, you can easily generate a project which uses ThreadX as RTOS and NetX Duo as network stack by point and click in STM32CubeMX. In this case, the project is generated to use CMake and the GCC toolchain.
NetX Duo requires some random numbers even for non-cryptography use cases. E.g. this code for allocating a random ephemeral TCP port so that the other endpoint doesn't think an old socket is reused when the MCU restarts for some reason:
Middlewares/ST/netxduo/common/src/nx_tcp_client_socket_bind.c:
UINT _nx_tcp_client_socket_bind(NX_TCP_SOCKET *socket_ptr, UINT port, ULONG wait_option)
{
...
/* Determine if the port needs to be allocated. */
if (port == NX_ANY_PORT)
{
/* Call the find routine to allocate a TCP port. */
port = NX_SEARCH_PORT_START + (UINT)(((ULONG)NX_RAND()) % ((NX_MAX_PORT + 1) - NX_SEARCH_PORT_START));
if (_nx_tcp_free_port_find(ip_ptr, port, &port) != NX_SUCCESS {
...
}
}
The configuration point for NX_RAND is in
Middlewares/ST/netxduo/common/inc/nx_api.h:
#ifndef NX_RAND
#ifdef NX_HIGH_SECURITY
#error "The symbol NX_RAND must be defined to use a qualified random number generator."
#else
#define NX_RAND rand
#endif
#endif
So NX_RAND() will call the rand provided by newlib. But by specification, rand is not necessarily safe for multi-thread use. It is often implemented as a weak PRNG using a global state variable (initialized by srand, otherwise as-if initialized to 1).
The newlib provided by the toolchain is compiled with retargetable locking which doesn't help here - there are no calls to the retargetable lock functions in newlib's implementation of rand.
newlib is also configured to provide re-entrancy by accessing state variables (last rand-value, errno, static buffers for strtok, asctime, ...) via a struct pointed to by _impure_ptr. The idea is that the system layer providing "threads" should update _impure_ptr when changing which thread is executing thereby giving each thread its own copies of the global variables.
To my understanding support for this is built into FreeRTOS and is enabled with configUSE_NEWLIB_REENTRANT (I have not used FreeRTOS myself so forgive me for any incompleteness here).
But STM32CubeMX for STM32H5 is not integrating with FreeRTOS but with ThreadX which doesn't have this support. See Newlib + threadx · Issue #448 · eclipse-threadx/threadx for some discussions from 2025 on how it could be done.
I.e. STM32CubeMX generated code which makes it easy to use multiple threads for network communication but if more than 1 of those thread calls _nx_tcp_client_socket_bind(..., NX_ANY_PORT, ...) you are in the danger zone of a race condition. This is a bit of disappointment for a tool that is intended to lower the barrier of entry and aims to provide a solution that works out of the box so you can focus on your application logic instead of libc system engineering issues.
One solution is to provide the necessary glue to make ThreadX correctly manage _impure_ptr.
This is probably a lot of work and most of the 40+ year old unsafe functions in the C library are not relevant in the embedded context with an RTOS anyway - there is no operating system, file system, time zones and you seldom have a MCU converting between wide and narrow characters. And if safe alternatives exist you as a developer can call those instead. But changing the ThreadX code to call rand_r with a thread-specific seed would break a lot of its APIs.
A better solution would be that STM32CubeMX generates defines for NX_RAND and NX_SRAND into the generated nx_user.h so that NX_RAND calls something like stm32_rand which can be implemented in a file in Core/Src (like sysmem.c and syscalls.c) and the implementation could wrap a call to newlib's rand_r in the locks you already have in stm32_lock.h. Together with a comment that if you need cryptographically-secure random numbers you need to do something better.
This still leaves the developer with the problem on how to seed the random number generator. But STM32CubeMX knows if the selected MCU has a RNG peripheral. So it could offer the developer to enable it and generate the necessary init code and initialize the shared (but not safely accessed) seed from a random number from this peripheral.
Or implement getentropy() in terms of calls to the RNG peripheral and define NX_RAND to use arc4random() which newlib provides and which is supposed to generate cryptographically-secure random numbers given proper seeding. arc4random() already uses the retargetable locking to protect the state.
Or implement NX_RAND as reading numbers from the RNG peripheral. I guess the choice of methods above depends on which is the most performant.
For MCUs that lack a RNG peripheral I guess a warning could be emitted that the developer has to solve the seeding himself (like reading a floating GPIO pin in analog mode or whatever based on the requirements on randomness for the specific project). This could just point to an application note document. I guess that going forward, only low cost MCU designs will lack a RNG peripheral and those will usually not implement use cases which need very random randomness anyway.
It is possible that this is solved in a better way with picolib which is maybe the way of the future anyway. But I haven't investigated.