Conversation
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #474
Scan targets checked: wolftpm-bugs, wolftpm-src
Findings: 4
Medium (2)
Unchecked sem_wait return value in TIS client IO callback
File: hal/tpm_io_fwtpm.c:198-199
Function: TPM2_IoCb_FwTPM
Category: Incorrect error handling
The sem_post and sem_wait return values are not checked. sem_wait can return -1 with errno set to EINTR if interrupted by a signal. When this occurs, the function proceeds to read from shm->reg_data as if the server has processed the request, but the server may not have completed (or even started) processing. This results in reading stale or incorrect data from the shared memory region, which could cause silent data corruption in TIS register reads.
/* Signal server and wait for completion */
sem_post((sem_t*)client->semCmd);
sem_wait((sem_t*)client->semRsp);
/* Copy result for reads */
if (isRead) {
XMEMCPY(buf, shm->reg_data, size);
}Recommendation: Check the return value of sem_wait and retry on EINTR. For example:
int ret;
do {
ret = sem_wait((sem_t*)client->semRsp);
} while (ret == -1 && errno == EINTR);
if (ret != 0) {
return TPM_RC_FAILURE;
}Unchecked sem_wait return allows use of stale shared memory data
File: hal/tpm_io_fwtpm.c:188-195
Function: TPM2_IoCb_FwTPM
Category: HAL implementation flaws
The sem_wait() call that waits for the fwTPM server response does not check its return value. sem_wait can fail with EINTR (signal interruption) or other errors, returning -1. When this happens, the function proceeds to XMEMCPY(buf, shm->reg_data, size) for read operations, copying potentially stale or uninitialized data from shared memory into the caller's buffer. For TIS register reads, this could return incorrect TPM status/data values, causing the TIS state machine in tpm2_tis.c to misinterpret TPM state. In a security context, this could cause command/response desynchronization or leak data from a previous TPM transaction.
/* Signal server and wait for completion */
sem_post((sem_t*)client->semCmd);
sem_wait((sem_t*)client->semRsp);
/* Copy result for reads */
if (isRead) {
XMEMCPY(buf, shm->reg_data, size);
}
return TPM_RC_SUCCESS;Recommendation: Check the return value of sem_wait(). If it returns -1, retry on EINTR (standard pattern: while (sem_wait(sem) == -1 && errno == EINTR) { }) and return TPM_RC_FAILURE for other errors. Similarly check sem_post() return value.
Low (2)
Static fwTPM client context never disconnected
File: hal/tpm_io_fwtpm.c:168-177
Function: TPM2_IoCb_FwTPM
Category: Resource leaks on error paths
The static gFwtpmClient is lazily initialized on first call via FWTPM_TIS_ClientConnect, which opens a shared memory mapping, a file descriptor, and two POSIX semaphores. However, FWTPM_TIS_ClientDisconnect is never called from any code path — there is no cleanup mechanism (e.g., atexit handler) to release these resources. While the OS reclaims these on process exit, the semaphore references (sem_close) are leaked if the library is dynamically loaded and unloaded without process termination.
static FWTPM_TIS_CLIENT_CTX gFwtpmClient;
static int gFwtpmClientInit = 0;
...
/* Lazy connect on first call */
if (!gFwtpmClientInit) {
int rc = FWTPM_TIS_ClientConnect(client);
if (rc != TPM_RC_SUCCESS) {
return rc;
}
gFwtpmClientInit = 1;
}Recommendation: Consider adding a cleanup function that calls FWTPM_TIS_ClientDisconnect(&gFwtpmClient) and resetting gFwtpmClientInit = 0. This could be registered via atexit() after successful connection, or called from TPM2_Cleanup or an equivalent teardown path.
No concurrency protection on shared memory register access fields
File: hal/tpm_io_fwtpm.c:178-195
Function: TPM2_IoCb_FwTPM
Category: HAL implementation flaws
The function writes multiple shared memory fields (reg_addr, reg_len, reg_is_write, reg_data) non-atomically before signaling the server via sem_post. If multiple threads or processes invoke TPM2_IoCb_FwTPM concurrently (e.g., in a multi-threaded application using wolfTPM), one caller's reg_addr/reg_len could be overwritten by another caller before the semaphore is posted. This would cause the server to process an incorrect register access, potentially reading/writing wrong TIS registers. The static gFwtpmClient global and gFwtpmClientInit flag compound this by sharing a single connection across all callers with no synchronization.
/* Fill register access request */
shm->reg_addr = addr;
shm->reg_len = size;
shm->reg_is_write = isRead ? 0 : 1;
if (!isRead) {
XMEMCPY(shm->reg_data, buf, size);
}
/* Signal server and wait for completion */
sem_post((sem_t*)client->semCmd);
sem_wait((sem_t*)client->semRsp);Recommendation: Add a mutex (e.g., pthread_mutex_t) to serialize access to the shared memory region across concurrent callers. Alternatively, document that this HAL is single-threaded only and add an assertion or compile-time guard.
This review was generated automatically by Fenrir. Findings are non-blocking.
- fwTPM server implementing TPM 2.0 spec v1.38 (98/113 commands, 87%) - NV storage with TLV journal format (flash-friendly, append-only) - Socket (mssim/swtpm auto-detect) and TIS/SHM transports for desktop - STM32 Cortex-M33 bare-metal port with TrustZone (CMSE) support - UART transport for embedded fwTPM communication - Shared crypto refactoring: KDFa/KDFe, AES-CFB, HMAC/Hash to tpm2_crypto.c - Bounds-checked TPM2_Packet_ParseU16Buf for safer response parsing - libFuzzer harness with gen_corpus.py and tpm2.dict - 290+ tpm2-tools compatibility tests - CI: fwtpm-test.yml (examples, tpm2-tools, emulator), fuzz.yml
…IGTERM handler
Primary Key Derivation (TPM 2.0 Part 1 Section 26):
- Derive RSA primary keys via iterative KDFa prime generation ("RSA p"/"RSA q")
- Derive ECC primary keys via KDFa scalar derivation, Q = d*G
- Derive KEYEDHASH/SYMCIPHER primary keys via KDFa ("KEYEDHASH"/"SYMCIPHER")
- Compute hashUnique = H(sensitiveCreate.data || unique) per Section 26.1
- Same seed + same template now always produces the same key (deterministic)
- Primary cache retained as performance optimization, no longer required
for correctness
ChangePPS / ChangeEPS (TPM 2.0 Part 3 Sections 24.4-24.5):
- ChangePPS: re-generate platform seed, flush platform cache entries
- ChangeEPS: re-generate endorsement seed, reset endorsement auth/policy,
flush endorsement cache entries
- Both require TPM_RH_PLATFORM authorization
Bug fixes:
- Flush stale primary cache entries in FwCmd_Clear when seeds are
regenerated (cache from old seed produced wrong keys)
- Add SIGTERM/SIGINT signal handler for graceful NV save on server kill
Code style:
- Remove 8 mid-function brace-scoped variable declaration blocks
- Deduplicate docs/FWTPM.md and src/fwtpm/README.md
Testing:
- tpm2-tools: ChangePPS/ChangeEPS positive tests, determinism-across-restart,
negative auth tests, endorsement auth reset verification (311 pass, 0 fail)
- Update command count: 105/113 v1.38 commands (93% coverage)
… real TPM's and reduce memory/NV use. Fix fuzzer report.
Summary
TPM2_Packet_ParseU16Bufvariant for safer response parsingfwTPM Server
Core TPM 2.0 command processing in
src/fwtpm/:fwtpm_command.c— 105 command handlers with full auth, sessions, parameter encryptionfwtpm_nv.c— TLV journal NV storage (file-based default, HAL-abstracted for flash)fwtpm_io.c— Socket transport (mssim + swtpm protocol auto-detection)fwtpm_tis.c/fwtpm_tis_shm.c— TIS register interface via POSIX shared memoryfwtpm_crypto.c— Key generation, sign/verify, seed encrypt/decrypt helpersBuild:
./configure --enable-fwtpm && makePrimary Key Derivation (TPM 2.0 Part 1 Section 26)
STM32 Port (
src/fwtpm/ports/stm32/)Bare-metal port for NUCLEO-H563ZI (Cortex-M33, 250MHz, 2MB flash, 640KB SRAM):
make TZEN=1)make SEMIHOSTING=1)make SELFTEST=1)UART Transport (
--enable-swtpm=uart)New transport option for wolfTPM client library to communicate with embedded fwTPM over serial:
./configure --enable-swtpm=uart— uses termios serial I/O instead of TCP socketsTPM2_SWTPM_HOSTenv var selects serial device at runtimeTesting
scripts/tpm2_tools_test.sh)examples/run_examples.sh)tests/fuzz/)scripts/fwtpm_emu_test.sh)Test plan
scripts/fwtpm_build_test.sh --all(examples + make check + 311 tpm2-tools)scripts/fwtpm_emu_test.sh(m33mu self-test with BKPT assertion)TPM2_SWTPM_HOST=/dev/ttyACM0 ./examples/wrap/caps