Skip to content

Add new Firmware TPM (fwTPM)#474

Open
dgarske wants to merge 3 commits intowolfSSL:masterfrom
dgarske:fwtpm
Open

Add new Firmware TPM (fwTPM)#474
dgarske wants to merge 3 commits intowolfSSL:masterfrom
dgarske:fwtpm

Conversation

@dgarske
Copy link
Copy Markdown
Contributor

@dgarske dgarske commented Mar 21, 2026

Summary

  • Add firmware TPM 2.0 server (fwTPM) implementing TPM 2.0 spec v1.38 (105/113 commands, 93% coverage)
  • Spec-compliant primary key derivation: deterministic RSA (iterative KDFa prime generation), ECC (KDFa scalar, Q=d*G), KEYEDHASH/SYMCIPHER keys — same seed + template always produces the same key
  • ChangePPS / ChangeEPS hierarchy commands with seed regeneration and cache flush
  • NV storage with TLV journal format (flash-friendly, append-only)
  • Socket (mssim) and TIS/SHM transports for desktop, UART transport for embedded
  • STM32 Cortex-M33 bare-metal port with TrustZone (CMSE) support
  • Shared crypto refactoring: extract KDFa/KDFe, AES-CFB, HMAC/Hash to tpm2_crypto.c
  • Bounds-checked TPM2_Packet_ParseU16Buf variant for safer response parsing
  • Auth validation: reject oversized auth values instead of silent truncation
  • SIGTERM/SIGINT signal handler for graceful NV save on server kill

fwTPM Server

Core TPM 2.0 command processing in src/fwtpm/:

  • fwtpm_command.c — 105 command handlers with full auth, sessions, parameter encryption
  • fwtpm_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 memory
  • fwtpm_crypto.c — Key generation, sign/verify, seed encrypt/decrypt helpers
  • Clock HAL with NV-persisted clockOffset

Build: ./configure --enable-fwtpm && make

Example: wolfSSL/wolftpm-examples#1

Primary Key Derivation (TPM 2.0 Part 1 Section 26)

  • RSA: iterative KDFa prime generation with labels "RSA p" / "RSA q"
  • ECC: KDFa scalar derivation, public point Q = d*G
  • KEYEDHASH/SYMCIPHER: KDFa with algorithm-specific labels
  • hashUnique = H(sensitiveCreate.data || unique) per Section 26.1
  • Primary cache retained as performance optimization, no longer required for correctness

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 sockets
  • TPM2_SWTPM_HOST env var selects serial device at runtime
  • Same mssim protocol as socket transport (compatible with all wolfTPM examples)

Testing

  • 311 tpm2-tools compatibility tests (scripts/tpm2_tools_test.sh)
  • Full wolfTPM example suite (examples/run_examples.sh)
  • libFuzzer harness with corpus generator (tests/fuzz/)
  • m33mu Cortex-M33 emulator CI test (scripts/fwtpm_emu_test.sh)
  • ASan / UBSan clean
  • 20 CI matrix configurations (pedantic gcc/clang -Werror, sanitizers, feature-disable variants)

wolfSSL-Fenrir-bot

This comment was marked as resolved.

@dgarske dgarske force-pushed the fwtpm branch 3 times, most recently from 9c0208f to eae465e Compare March 21, 2026 23:51
@dgarske dgarske changed the title Add fwTPM firmware TPM 2.0 server with STM32 port Add fwTPM firmware TPM 2.0 server Mar 21, 2026
@dgarske dgarske changed the title Add fwTPM firmware TPM 2.0 server Add new Firmware TPM (fwTPM) Mar 22, 2026
@dgarske dgarske force-pushed the fwtpm branch 2 times, most recently from b186ecc to f529484 Compare March 23, 2026 21:28
@dgarske dgarske assigned wolfSSL-Bot and unassigned dgarske and aidangarske Mar 24, 2026
@dgarske dgarske assigned dgarske and unassigned wolfSSL-Bot Mar 25, 2026
wolfSSL-Fenrir-bot

This comment was marked as resolved.

wolfSSL-Fenrir-bot

This comment was marked as resolved.

This comment was marked as resolved.

@aidangarske aidangarske requested a review from danielinux March 25, 2026 20:51
Copilot AI review requested due to automatic review settings April 3, 2026 17:42

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 8, 2026 19:05

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 8, 2026 22:06

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 9, 2026 02:47

This comment was marked as resolved.

This comment was marked as resolved.

This comment was marked as resolved.

@dgarske dgarske removed their assignment Apr 10, 2026
aidangarske

This comment was marked as duplicate.

aidangarske

This comment was marked as resolved.

aidangarske

This comment was marked as resolved.

aidangarske

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 13, 2026 18:52

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings April 13, 2026 21:56
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 61 out of 80 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@aidangarske aidangarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: review-security
Overall recommendation: COMMENT
Findings: 19 total — 11 posted, 8 skipped
11 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [Medium] wolfTPM2_SetSessionHandle mutates session->auth.size before bounds check, leaving session inconsistent on BUFFER_Esrc/tpm2_wrap.c:1865-1872
  • [Medium] wolfTPM2_SetIdentityAuth copies uninitialized stack data to handle auth on hash failuresrc/tpm2_wrap.c:9254-9255
  • [Medium] XOR param encryption mask uses 1500-byte stack buffer not guarded by WOLFTPM_SMALL_STACKsrc/tpm2_param_enc.c:62-76
  • [Medium] TPM2_AesCfbEncrypt/Decrypt missing WOLFSSL_AES_CFB compile guardsrc/tpm2_crypto.c:332-397
  • [Medium] UART VMIN=0 allows per-byte timeout reset enabling indefinite blocking in SwTpmReceivesrc/tpm2_swtpm.c:135-158
  • [Medium] UART SwTpmDisconnect leaves FD valid after SESSION_END write failure, preventing reconnectionsrc/tpm2_swtpm.c:370-374
  • [Medium] Auto-enable of fwtpm+swtpm on Linux x86_64 breaks builds without required wolfSSL featuresconfigure.ac:228-246
  • [Low] tpm2_crypto.h declares crypto primitives without WOLFTPM2_NO_WOLFCRYPT guardwolftpm/tpm2_crypto.h:145-265
  • [Low] cfsetospeed/cfsetispeed return values unchecked in UART termios configurationsrc/tpm2_swtpm.c:223-224
  • [Low] distcheck auto-enables fwtpm on Linux x86_64, changing existing behaviorMakefile.am:31
  • [Info] WOLFTPM_FWTPM vs WOLFTPM_FWTPM_BUILD separation correctly implementedconfigure.ac:612-618

Skipped findings

  • [Low] getenv(TPM2_SWTPM_HOST) allows opening arbitrary character devices in UART mode
  • [Low] wolfTPM2_SetAuthHandle policyAuth flag mutated before bounds check
  • [Low] wolfTPM2_EncryptSecret_RSA plaintext secret not zeroized on error after generation
  • [Low] TPM2_AesCfbEncrypt/Decrypt do not zeroize stack Aes object after use
  • [Low] TPM2_HmacCompute does not zeroize stack Hmac object containing key material
  • [Low] ByteArrayToU16/U32/U64 load helpers lack NULL pointer checks unlike store counterparts
  • [Info] TPM2_ConstantCompare volatile pattern is correct and improved
  • [Info] TPM2_ParamEnc_AESCFB returns mixed error code domains

Review generated by Skoll

#ifdef DEBUG_WOLFTPM
printf("SetSessionHandle: auth size %d exceeds buffer %d\n",
session->auth.size, (int)sizeof(session->auth.buffer));
#endif
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] wolfTPM2_SetSessionHandle mutates session->auth.size before bounds check, leaving session inconsistent on BUFFER_E
Category: Logic

In wolfTPM2_SetSessionHandle, line 1867 assigns session->auth.size = tpmSession->handle.auth.size BEFORE the bounds check on line 1868 (if (session->auth.size > sizeof(session->auth.buffer))). If the size exceeds the buffer, the function returns BUFFER_E, but session->auth.size has already been set to the oversized value and session->sessionHandle was already mutated on line 1865. This is the exact pattern that wolfTPM2_SetAuth was explicitly fixed to avoid in the same PR (lines 1664-1669 validate bounds BEFORE mutating session state). Any subsequent code that ignores the BUFFER_E return and reads session->auth using session->auth.size as the length would read past session->auth.buffer.

session->sessionHandle = tpmSession->handle.hndl;

session->auth.size = tpmSession->handle.auth.size;
if (session->auth.size > sizeof(session->auth.buffer)) {
    return BUFFER_E;
}

Recommendation: Move the bounds check before assigning session->auth.size. Check tpmSession->handle.auth.size directly against sizeof(session->auth.buffer) before any session field mutations, matching the pattern used in wolfTPM2_SetAuth.

Comment on lines +62 to +76
#define TPM2_XOR_MASK_MAX 1500
#endif
}


/* Perform XOR encryption over the first parameter of a TPM packet */
int TPM2_ParamEnc_XOR(TPM2_AUTH_SESSION *session, TPM2B_AUTH* sessKey,
TPM2B_AUTH* bindKey, TPM2B_NONCE* nonceCaller, TPM2B_NONCE* nonceTPM,
/* XOR parameter encryption/decryption (shared by client and fwTPM).
* XOR is symmetric so encrypt and decrypt are the same operation.
* nonceA/nonceB order determines direction (caller/TPM or TPM/caller). */
int TPM2_ParamEnc_XOR(
TPMI_ALG_HASH authHash,
const BYTE *keyIn, UINT32 keyInSz,
const BYTE *nonceA, UINT32 nonceASz,
const BYTE *nonceB, UINT32 nonceBSz,
BYTE *paramData, UINT32 paramSz)
{
int rc = TPM_RC_FAILURE;
TPM2B_DATA keyIn;
TPM2B_MAX_BUFFER mask;
int rc;
BYTE mask[TPM2_XOR_MASK_MAX];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] XOR param encryption mask uses 1500-byte stack buffer not guarded by WOLFTPM_SMALL_STACK
Category: Logic

The refactored TPM2_ParamEnc_XOR allocates a 1500-byte mask buffer on the stack (BYTE mask[TPM2_XOR_MASK_MAX]). The old code used TPM2B_MAX_BUFFER mask which was ~1026 bytes (2 + MAX_DIGEST_BUFFER=1024). This increases stack usage by ~474 bytes for a function in the hot path of every parameter-encrypted TPM command. The WOLFTPM_SMALL_STACK define is used extensively in the fwTPM code to heap-allocate large objects, but is not applied here. On embedded targets with limited stack (e.g., 4-8KB), this 1500-byte allocation combined with caller stack frames could cause stack overflow.

#ifndef TPM2_XOR_MASK_MAX
#define TPM2_XOR_MASK_MAX 1500
#endif
...
BYTE mask[TPM2_XOR_MASK_MAX];

Recommendation: Either reduce TPM2_XOR_MASK_MAX to match old behavior (1024 bytes matches MAX_DIGEST_BUFFER), or guard the stack allocation with WOLFTPM_SMALL_STACK to heap-allocate when configured for constrained environments.

Comment on lines +332 to +397
#ifndef NO_AES

int TPM2_AesCfbEncrypt(
const byte* key, int keySz,
const byte* iv,
byte* data, word32 dataSz)
{
int rc;
Aes aes;
byte zeroIV[AES_BLOCK_SIZE];

if (key == NULL || (data == NULL && dataSz > 0)) {
return BAD_FUNC_ARG;
}
if (keySz != 16 && keySz != 24 && keySz != 32) {
return BAD_FUNC_ARG;
}

if (iv == NULL) {
XMEMSET(zeroIV, 0, sizeof(zeroIV));
iv = zeroIV;
}

rc = wc_AesInit(&aes, NULL, INVALID_DEVID);
if (rc == 0) {
rc = wc_AesSetKey(&aes, key, (word32)keySz, iv, AES_ENCRYPTION);
if (rc == 0) {
rc = wc_AesCfbEncrypt(&aes, data, data, dataSz);
}
wc_AesFree(&aes);
}
return rc;
}

int TPM2_AesCfbDecrypt(
const byte* key, int keySz,
const byte* iv,
byte* data, word32 dataSz)
{
int rc;
Aes aes;
byte zeroIV[AES_BLOCK_SIZE];

if (key == NULL || (data == NULL && dataSz > 0)) {
return BAD_FUNC_ARG;
}
if (keySz != 16 && keySz != 24 && keySz != 32) {
return BAD_FUNC_ARG;
}

if (iv == NULL) {
XMEMSET(zeroIV, 0, sizeof(zeroIV));
iv = zeroIV;
}

rc = wc_AesInit(&aes, NULL, INVALID_DEVID);
if (rc == 0) {
rc = wc_AesSetKey(&aes, key, (word32)keySz, iv, AES_ENCRYPTION);
if (rc == 0) {
rc = wc_AesCfbDecrypt(&aes, data, data, dataSz);
}
wc_AesFree(&aes);
}
return rc;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] TPM2_AesCfbEncrypt/Decrypt missing WOLFSSL_AES_CFB compile guard
Category: Logic

New functions TPM2_AesCfbEncrypt and TPM2_AesCfbDecrypt in tpm2_crypto.c are guarded only by #ifndef NO_AES but call wc_AesCfbEncrypt/wc_AesCfbDecrypt which require WOLFSSL_AES_CFB to be defined in wolfSSL. In a build configuration where AES is enabled but AES-CFB mode is not (i.e., !NO_AES but !WOLFSSL_AES_CFB), these functions will fail to compile because wc_AesCfbEncrypt/wc_AesCfbDecrypt are undeclared. The sister function TPM2_ParamEnc_AESCFB in tpm2_param_enc.c correctly uses #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB). The header tpm2_crypto.h also lacks this guard at line 147.

#ifndef WOLFTPM2_NO_WOLFCRYPT
#ifndef NO_AES
int TPM2_AesCfbEncrypt(...) {
    ...
    rc = wc_AesCfbEncrypt(&aes, data, data, dataSz);  /* requires WOLFSSL_AES_CFB */

Recommendation: Change the guard in tpm2_crypto.c from #ifndef NO_AES to #if !defined(NO_AES) && defined(WOLFSSL_AES_CFB), and apply the same change in tpm2_crypto.h around the function declarations.

src/tpm2_swtpm.c Outdated
Comment on lines +370 to +374
#ifdef WOLFTPM_SWTPM_UART
/* UART: keep the port open for the next command.
* The SESSION_END tells the server the command sequence is done.
* Final cleanup of the UART FD is handled in TPM2_SwtpmCloseUART. */
(void)ctx;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] UART SwTpmDisconnect leaves FD valid after SESSION_END write failure, preventing reconnection
Category: Resource Leak

In SwTpmDisconnect with UART mode, if SwTpmTransmit of TPM_SESSION_END fails, the function returns an error but leaves ctx->tcpCtx.fd unchanged. On the next TPM2_SWTPM_SendCommand call, fd >= 0 causes it to skip reconnection and reuse the broken connection. Unlike the socket path which always closes/resets FD to -1, UART has no recovery.

#ifdef WOLFTPM_SWTPM_UART
    /* UART: keep the port open for the next command. */
    (void)ctx;
#else
    if (0 != close(ctx->tcpCtx.fd)) { ... }
    ctx->tcpCtx.fd = -1;
#endif

Recommendation: When SwTpmTransmit of SESSION_END fails in the UART path, close the FD and reset ctx->tcpCtx.fd to -1 to force reconnection on the next command.

configure.ac Outdated
Comment on lines +228 to +246
# Native host defaults — auto-enable fwTPM and swTPM on Linux/BSD x86_64 / aarch64
# so `make check` provides full coverage out of the box. Users can still
# explicitly disable with --disable-fwtpm / --disable-swtpm.
WOLFTPM_DEFAULT_FWTPM=no
WOLFTPM_DEFAULT_SWTPM=no
case $host_cpu in
x86_64|amd64|aarch64)
# Defensive exclusion: fwtpm_server uses POSIX sockets and is not
# currently portable to Windows / Darwin. Auto-enable on Linux/BSD only.
case $host_os in
*mingw*|*cygwin*|*msys*|*darwin*|*win32*)
;;
*)
WOLFTPM_DEFAULT_FWTPM=yes
WOLFTPM_DEFAULT_SWTPM=yes
;;
esac
;;
esac
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Auto-enable of fwtpm+swtpm on Linux x86_64 breaks builds without required wolfSSL features
Category: Logic

On Linux x86_64/aarch64, configure.ac auto-enables both FWTPM and SWTPM by default. fwtpm_server requires wolfSSL with WOLFSSL_KEY_GEN, WC_RSA_NO_PADDING, HAVE_PK_CALLBACKS, and WOLFSSL_PUBLIC_MP, but configure only checks that wolfCrypt_Init exists. A user with a minimal wolfSSL install gets a silently degraded fwTPM server where key generation is ifdef'd out and RSA operations fail at runtime.

case $host_cpu in
    x86_64|amd64|aarch64)
        WOLFTPM_DEFAULT_FWTPM=yes
        WOLFTPM_DEFAULT_SWTPM=yes

Recommendation: When ENABLED_FWTPM=yes, add configure-time checks for WC_RSA_NO_PADDING and WOLFSSL_KEY_GEN. Emit AC_MSG_WARN if not found.

src/tpm2_swtpm.c Outdated
Comment on lines +223 to +224
cfsetospeed(&tty, baud);
cfsetispeed(&tty, baud);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] cfsetospeed/cfsetispeed return values unchecked in UART termios configuration
Category: Logic

In the UART path of SwTpmConnect, cfsetospeed() and cfsetispeed() return values are not checked. If they fail, tcsetattr() applies a partially configured termios struct, potentially causing communication at an unexpected baud rate.

cfsetospeed(&tty, baud);
cfsetispeed(&tty, baud);
// no return value check

Recommendation: Check return values of cfsetospeed and cfsetispeed. On failure, close fd and return TPM_RC_FAILURE.

Comment on lines 612 to +618
OPTION_FLAGS="$CFLAGS $CPPFLAGS $AM_CFLAGS"

# Add fwTPM build marker to options.h for test script detection.
# Uses WOLFTPM_FWTPM_BUILD (not WOLFTPM_FWTPM which gates server code
# in tpm2_packet.c/tpm2_param_enc.c and must only be set for the server target).
if test "x$ENABLED_FWTPM" = "xyes"; then
OPTION_FLAGS="$OPTION_FLAGS -DWOLFTPM_FWTPM_BUILD"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ [Info] WOLFTPM_FWTPM vs WOLFTPM_FWTPM_BUILD separation correctly implemented
Category: Logic

The PR correctly separates WOLFTPM_FWTPM (per-target compile flag for server code) from WOLFTPM_FWTPM_BUILD (options.h marker for test detection). WOLFTPM_FWTPM is only set via per-target CFLAGS for fwtpm_server/fwtpm_fuzz/fwtpm_unit.test. It cannot leak into libwolftpm.la. The CMakeLists.txt mirrors this correctly.

Recommendation: No action needed. The design is sound.

src/tpm2_wrap.c Outdated
@@ -9370,6 +9258,9 @@
TPM2_PrintBin(handle->auth.buffer, handle->auth.size);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] wolfTPM2_SetIdentityAuth copies uninitialized stack data to handle auth on hash failure
Category: Logic

In wolfTPM2_SetIdentityAuth, if any hash operation (wc_HashInit/Update/Final) fails, rc is non-zero but execution falls through to lines 9254-9255 which unconditionally set handle->auth.size = 16 and copy 16 bytes from the digest stack buffer into handle->auth.buffer. The digest array is a stack-local uint8_t digest[TPM_SHA256_DIGEST_SIZE] without initialization. If wc_HashFinal was never called, the digest contains whatever was on the stack. The new TPM2_ForceZero calls on lines 9261-9262 wipe the stack copies, but the garbage value has already been copied into handle->auth.buffer. The PR touches this function by adding ForceZero calls, making this in-scope.

handle->auth.size = 16;
    XMEMCPY(handle->auth.buffer, digest, 16);

    TPM2_ForceZero(digest, sizeof(digest));
    TPM2_ForceZero(serialNum, sizeof(serialNum));

Recommendation: Guard lines 9254-9255 with if (rc == 0) so the handle auth is only set when hash operations succeeded. On failure, set handle->auth.size = 0.


Note: Referenced line (src/tpm2_wrap.c:9254-9255) is outside the diff hunk. Comment anchored to nearest changed region.

#endif

return rc;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] UART VMIN=0 allows per-byte timeout reset enabling indefinite blocking in SwTpmReceive
Category: Logic

With VMIN=0 and VTIME=200 (20 seconds), each read() call starts its own independent 20-second timer. SwTpmReceive loops calling read() until all bytes_remaining are received. A malicious or malfunctioning UART device can send one byte every ~19 seconds, resetting the timer each time. For a 4096-byte response, this extends the effective timeout to ~22 hours instead of 20 seconds.

tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 200; /* 20 second timeout */
// SwTpmReceive loops: while (bytes_remaining > 0) { wrc = read(...); ... }

Recommendation: Track wall-clock time across the entire SwTpmReceive loop using clock_gettime(CLOCK_MONOTONIC) and abort if total elapsed time exceeds a maximum threshold.


Note: Referenced line (src/tpm2_swtpm.c:135-158) is outside the diff hunk. Comment anchored to nearest changed region.

@@ -47,6 +47,7 @@ include docs/include.am
include wrapper/include.am
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] distcheck auto-enables fwtpm on Linux x86_64, changing existing behavior
Category: Logic

AM_DISTCHECK_CONFIGURE_FLAGS does not pass --disable-fwtpm. On Linux x86_64, the auto-enable logic will enable fwtpm during distcheck, changing existing distcheck behavior and potentially failing if wolfSSL lacks required features.

AM_DISTCHECK_CONFIGURE_FLAGS = --enable-swtpm @DISTCHECK_SWTPM_PORT_FLAG@

Recommendation: Add --disable-fwtpm to AM_DISTCHECK_CONFIGURE_FLAGS, or document the changed behavior.


Note: Referenced line (Makefile.am:31) is outside the diff hunk. Comment anchored to nearest changed region.

Copilot AI review requested due to automatic review settings April 14, 2026 21:46
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 52 out of 68 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

CMakeLists.txt:727

  • Installing/exporting the wolftpm_wolfssl_dep INTERFACE target is likely unintended and can complicate downstream find_package(wolftpm) usage (it also doesn’t represent an installable artifact). Prefer exporting only wolftpm (and any real binaries like fwtpm_server) and model the wolfSSL dependency via usage requirements on wolftpm (or by generating a config that finds wolfSSL). If you keep an INTERFACE helper, consider avoiding install(TARGETS ...) for it.
if(BUILD_WOLFTPM_LIB)
    install(TARGETS wolftpm wolftpm_wolfssl_dep
            EXPORT  wolftpm-targets
            LIBRARY DESTINATION lib
            ARCHIVE DESTINATION lib
            RUNTIME DESTINATION bin
            )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +258 to +291
case FWTPM_TIS_DATA_FIFO:
case FWTPM_TIS_XDATA_FIFO: {
/* Read response data from FIFO */
UINT32 i;
UINT32 avail;
/* Snapshot read state to locals for TOCTOU safety */
UINT32 rpos = regs->fifo_read_pos;
UINT32 rlen = regs->rsp_len;
if (rpos > rlen || rpos >= sizeof(regs->rsp_buf)) {
avail = 0;
}
else {
avail = rlen - rpos;
}
if (len > avail) {
len = avail;
}
if (len > sizeof(regs->reg_data)) {
len = (UINT32)sizeof(regs->reg_data);
}
for (i = 0; i < len; i++) {
regs->reg_data[i] = regs->rsp_buf[rpos++];
}
regs->fifo_read_pos = rpos;
/* Update data availability */
if (rpos >= rlen) {
/* All response bytes read */
regs->sts = TisBuildSts(
FWTPM_STS_VALID | FWTPM_STS_COMMAND_READY,
FWTPM_TIS_BURST_COUNT);
}
/* Return early - data already in reg_data */
return;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server clamps len (and may return early in FIFO reads) but it does not clear the full regs->reg_data buffer. Since the client side copies the originally requested size bytes, a read request larger than len can leak stale bytes left in shared memory from prior operations. Fix by zeroing regs->reg_data for its full size on every read path before writing the response bytes (and/or by writing back the effective length and having the client copy only that length).

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +78
int openFlags = O_CREAT | O_RDWR | O_TRUNC | O_NOFOLLOW;

#ifdef O_CLOEXEC
openFlags |= O_CLOEXEC;
#endif

/* Create shared memory file */
fd = open(FWTPM_TIS_SHM_PATH, openFlags, 0600);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O_NOFOLLOW is used unconditionally, but it is not available on all POSIX targets/stdlib combinations. This can cause build failures on platforms where the macro is missing. Guard O_NOFOLLOW with #ifdef O_NOFOLLOW (or add a local fallback #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif, consistent with the UART transport code) to keep compilation portable.

Copilot uses AI. Check for mistakes.
client->shmFd = -1;

/* Open existing shared memory file */
fd = open(FWTPM_TIS_SHM_PATH, O_RDWR | O_NOFOLLOW | O_CLOEXEC);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O_CLOEXEC is not guaranteed to be defined on all supported platforms/toolchains. Since this file is compiled as part of the client library (not just fwTPM), an unconditional O_CLOEXEC can break builds. Use an #ifdef O_CLOEXEC guarded flag (and, if needed, fall back to setting FD_CLOEXEC via fcntl) to preserve portability.

Copilot uses AI. Check for mistakes.
Comment on lines +117 to 131
ptr = (const char*)buffer;
remaining = bufSz;
while (remaining > 0) {
wrc = write(ctx->tcpCtx.fd, ptr, remaining);
if (wrc <= 0) {
#ifdef WOLFTPM_DEBUG_VERBOSE
printf("Failed to send the TPM command to fd %d, got errno %d ="
"%s\n", ctx->tcpCtx.fd, errno, strerror(errno));
#endif
rc = TPM_RC_FAILURE;
break;
}
remaining -= wrc;
ptr += wrc;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the new transmit loop, write() failures due to EINTR (and potentially EAGAIN depending on FD settings) are treated as hard errors. This can make UART/socket communication flaky under signal delivery or non-blocking configurations. Consider retrying on EINTR, and handling EAGAIN/EWOULDBLOCK with a bounded wait (poll/select) if those modes are possible for this transport.

Copilot uses AI. Check for mistakes.
endif()

# wolfSSL/wolfCrypt dependency (shared between wolftpm library and fwtpm targets)
add_library(wolftpm_wolfssl_dep INTERFACE)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing/exporting the wolftpm_wolfssl_dep INTERFACE target is likely unintended and can complicate downstream find_package(wolftpm) usage (it also doesn’t represent an installable artifact). Prefer exporting only wolftpm (and any real binaries like fwtpm_server) and model the wolfSSL dependency via usage requirements on wolftpm (or by generating a config that finds wolfSSL). If you keep an INTERFACE helper, consider avoiding install(TARGETS ...) for it.

Copilot uses AI. Check for mistakes.
Comment on lines +733 to +734
NAMESPACE wolfssl::)
endif()
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing/exporting the wolftpm_wolfssl_dep INTERFACE target is likely unintended and can complicate downstream find_package(wolftpm) usage (it also doesn’t represent an installable artifact). Prefer exporting only wolftpm (and any real binaries like fwtpm_server) and model the wolfSSL dependency via usage requirements on wolftpm (or by generating a config that finds wolfSSL). If you keep an INTERFACE helper, consider avoiding install(TARGETS ...) for it.

Copilot uses AI. Check for mistakes.
configure.ac Outdated
Comment on lines +228 to +246
# Native host defaults — auto-enable fwTPM and swTPM on Linux/BSD x86_64 / aarch64
# so `make check` provides full coverage out of the box. Users can still
# explicitly disable with --disable-fwtpm / --disable-swtpm.
WOLFTPM_DEFAULT_FWTPM=no
WOLFTPM_DEFAULT_SWTPM=no
case $host_cpu in
x86_64|amd64|aarch64)
# Defensive exclusion: fwtpm_server uses POSIX sockets and is not
# currently portable to Windows / Darwin. Auto-enable on Linux/BSD only.
case $host_os in
*mingw*|*cygwin*|*msys*|*darwin*|*win32*)
;;
*)
WOLFTPM_DEFAULT_FWTPM=yes
WOLFTPM_DEFAULT_SWTPM=yes
;;
esac
;;
esac
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the default configure behavior on common Linux/BSD hosts to auto-enable both --enable-fwtpm and --enable-swtpm. The PR description focuses on adding fwTPM, but doesn’t call out this default-behavior change, which can affect existing users/builds (e.g., new optional dependency expectations and different make check behavior). Consider explicitly documenting this change in the PR description and/or project docs (or defaulting to disabled and enabling only in CI/scripts).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants