The test subject: Public on GitHub, commit c3d1f92 (GPL-3.0)
What is an STM32 project made of?
A project from STM32CubeMX or STM32CubeIDE takes code from the STM32Cube package of the chip family: the HAL driver, the chip's CMSIS files, board drivers and middlewares. Under Middlewares/Third_Party it holds libraries from other vendors, in STM32CubeF4 for example FreeRTOS, mbedTLS and LwIP. The linker decides which of them end up in the device.
For the SBOM you therefore need two answers: which version of the package the project uses, and which libraries from it are in the build.
| Part | Where the version is |
|---|---|
| Cube package | Project file .ioc, line ProjectManager.FirmwarePackage |
| HAL driver | stm32f4xx_hal.c, macro __STM32F4xx_HAL_VERSION |
| FreeRTOS | task.h, macro tskKERNEL_VERSION_NUMBER |
| mbedTLS | mbedtls/version.h, MBEDTLS_VERSION_STRING |
| LwIP | lwip/init.h, LWIP_VERSION_MAJOR to _REVISION |
| Compiler | Section .comment in the ELF |
Step 1: Which Cube package version does my project use?
The STM32CubeMX project file names the chip and the package version. With the package version you know which middleware versions ST shipped.
grep -E "FirmwarePackage=|DeviceId=" Discovery_Oscilloscope.iocOutput
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DeviceId=STM32F429ZITx
ProjectManager.FirmwarePackage=STM32Cube FW_F4 V1.26.2Step 2: Which middleware versions does the package ship?
The versions of the shipped libraries are in their headers. Shown here on the current release STM32CubeF4 v1.28.3, run in the package directory.
ls Middlewares/Third_PartyOutput
FatFs
FreeRTOS
LibJPEG
LwIP
mbedTLSgrep -h -m1 -E "tskKERNEL_VERSION_NUMBER|MBEDTLS_VERSION_STRING |FatFs - |JVERSION" \
Middlewares/ Third_Party/ FreeRTOS/ Source/ include/ task.h \
Middlewares/ Third_Party/ mbedTLS/ include/ mbedtls/ version.h \
Middlewares/Third_Party/FatFs/src/ff.h \
Middlewares/ Third_Party/ LibJPEG/ include/ jversion.hOutput
#define tskKERNEL_VERSION_NUMBER "V10.3.1"
#define MBEDTLS_VERSION_STRING "2.16.2"
/ FatFs - Generic FAT file system module R0.12c /
#define JVERSION "8d 15-Jan-2012"jq -r ".components[].name" sbom_cdx.json \
| grep -ciE "freertos|lwip|mbedtls|fatfs|libjpeg"0Step 3: What of it is really in the build?
The ELF symbol table shows which libraries are linked. In the test subject these are the HAL, the CMSIS-RTOS2 layer and the FreeRTOS kernel. A library from the package that leaves no function in the ELF does not belong in this device's SBOM.
Here the FreeRTOS version is in the ELF itself, because the debug build is compiled with -g3. According to GCC this level puts all macro definitions into the debug information; in the test subject the text sits in the .debug_str section. A build without debug information no longer has this line. Then you take the version from step 2.
nm STM32F429I-Oscilloscope.elf \
| grep -E " T (HAL_Init|osKernelStart|vTaskStartScheduler|xTaskCreate)$"Output
08004ee9T HAL_Init08007f31T osKernelStart0800964dT vTaskStartScheduler080095f1T xTaskCreatestrings -a STM32F429I-Oscilloscope.elf | grep "tskKERNEL_VERSION_NUMBER"Output
RTOS_ID_s ("FreeRTOS " tskKERNEL_VERSION_NUMBER)
tskKERNEL_VERSION_NUMBER "V10.3.1"
KERNEL_ID ("FreeRTOS " tskKERNEL_VERSION_NUMBER)strings -a STM32F429I-Oscilloscope.elf | grep -m1 "GCC: "Output
GCC: (GNU Tools for STM32 9-2020-q2-update.20201001-1621) 9.3.1 20200408 (release)Step 4: What does the GCC map file show?
STM32CubeIDE compiles with -ffunction-sections -fdata-sections and links with --gc-sections. So every function sits in its own section, and the linker removes every section nothing uses. The map file (-Wl,-Map=) shows both: under “Archive member included” every library member that entered the image, with the symbol that pulled it in, and under “Discarded input sections” everything that was removed.
In the test subject the linker removes 37 functions from FreeRTOS tasks.c alone, among them vTaskGetInfo. For a CVE in FreeRTOS, check here whether the affected function is in the image or among the removed ones.
grep -o -- "-Wl,--gc-sections" makefilegrep -o -- "-ffunction-sections -fdata-sections" subdir.mk | sort -uOutput
-Wl,--gc-sections
-ffunction-sections -fdata-sectionssed -n '/^Archive member included/,/^Allocating common/p' \
STM32F429I-Oscilloscope.map \
| grep -o '[A-Za-z0-9_+-]*\.a(' | sort | uniq -cOutput
76libc_nano.a(6libgcc.a(15libstdc++_nano.a(57libtouchgfx-float-abi-hard.a(grep -A1 "^ \.text\.vTaskGetInfo" STM32F429I-Oscilloscope.mapnm STM32F429I-Oscilloscope.elf | grep -c "vTaskGetInfo"Output
.text.vTaskGetInfo
0x0000000000000000 0x7c ./Middlewares/FreeRTOS/tasks.o
0awk '/^Discarded input sections/,/^Memory Configuration/' \
STM32F429I-Oscilloscope.map \
| grep -A1 '^ \.text\.' | grep -c 'Middlewares/FreeRTOS/tasks.o'37Where does the version of a precompiled library like TouchGFX come from?
The test subject links the TouchGFX graphics library as a finished archive, libtouchgfx-float-abi-hard.a. The ELF carries 1,092 symbols with touchgfx, but no version number. The version is in the project file: X-CUBE-TOUCHGFX 4.17.0.
For the SBOM this means: name from the ELF, version from the .ioc, with a note on where it comes from. The same holds for every library a vendor ships only as .a or .lib.
The BIN of the same build does not name the FreeRTOS version either. In the debug ELF it is only in the debug information.
grep -o "X-CUBE-TOUCHGFX\.[0-9.]*[0-9]" Discovery_Oscilloscope.ioc | sort -unm STM32F429I-Oscilloscope.elf | grep -c "touchgfx"strings -a STM32F429I-Oscilloscope.elf | grep -c "4\.17"Output
X-CUBE-TOUCHGFX.4.17.0
1092
0strings -a STM32F429I-Oscilloscope.bin | grep -c "V10.3.1"0Can this run automatically?
Microcontroller builds from Keil MDK, IAR or GNU Arm. It finds the libraries built in, with their versions, for example CMSIS packs, RTX5, FreeRTOS, lwIP, mbedTLS and STM32Cube. Plus hardening (stack canary, MPU, privilege separation) and embedded keys.
Every component with an evidenced version is matched against the vulnerability sources. If the file evidences no version, the field stays blank. You export the SBOM as CycloneDX or SPDX.
Questions
Is the SBOM that ST ships with the Cube package enough?
Not for your device. It describes the package, and in STM32CubeF4 v1.28.3 it lacks the five libraries under Middlewares/Third_Party. Your SBOM names what your build linked, with the version.
Which file from STM32CubeIDE do I use?
The .elf from the build folder, such as Debug or Release. Add the project's .ioc, which names the package version.
Does this apply to Keil and IAR too?
Yes. Keil writes an .axf, IAR an .out, and both are ELF files. The middlewares come from the same Cube package.
What does --gc-sections mean for the SBOM?
The linker removes functions nothing calls. A library is then in the SBOM, but not every one of its functions is in the device. In the example 37 functions from FreeRTOS tasks.c are missing. For a reported vulnerability, map file and symbol table show whether the affected code is linked.
Where does the version of a library shipped only as .a come from?
From the project file, the package or the vendor's release notes. In the example the .ioc names TouchGFX 4.17.0; the ELF names only functions.
Does the HAL driver belong in the SBOM?
Yes. It is third-party code that runs in the device. In Annex I Part II the CRA requires the components to be identified, at least the top-level dependencies.
Sources
- [1]STMicroelectronics: STM32CubeF4, release v1.28.3
- [2]Test subject: mmahdim/STM32F4-Scope, commit c3d1f92
- [3]Regulation (EU) 2024/2847, Cyber Resilience Act
- [4]BSI TR-03183-2, version 2.1.0
- [5]GNU Binutils: nm and strings
- [6]GNU ld: --gc-sections and -Map
- [7]GCC: -ffunction-sections, -fdata-sections
- [8]GCC: -g3, macros in the debug information