From 51475032c94d3a483edeccb2d7127fe6383fe47f Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 4 Aug 2026 11:37:01 -0700 Subject: [PATCH 1/5] F-7606 - Skip redundant NV_ReadPublic in NV write chunk loop --- examples/tpm_test.h | 1 + src/tpm2_wrap.c | 17 ++++---- tests/unit_tests.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 7 deletions(-) diff --git a/examples/tpm_test.h b/examples/tpm_test.h index e69079b4d..765dfaac6 100644 --- a/examples/tpm_test.h +++ b/examples/tpm_test.h @@ -45,6 +45,7 @@ #define TPM2_DEMO_NV_TEST_INDEX 0x01800200 #define TPM2_DEMO_NV_TEST_AUTH_INDEX 0x01800201 #define TPM2_DEMO_NVRAM_STORE_INDEX 0x01800202 +#define TPM2_DEMO_NV_TEST_CHUNKED_INDEX 0x01800205 #define TPM2_DEMO_NVRAM_EXTEND_INDEX 0x01000200 #define TPM2_DEMO_NV_TEST_SIZE MAX_DIGEST_BUFFER /* max size on Infineon SLB9670 is 1664 */ #define TPM2_DEMO_NV_COUNTER_INDEX 0x01800300 diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 041931b09..6de0d5383 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -7103,13 +7103,16 @@ static int wolfTPM2_NVWriteData(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* tpmSession, if (towrite > MAX_NV_BUFFER_SIZE) towrite = MAX_NV_BUFFER_SIZE; - /* Make sure the name is computed for the handle. - * Name changes on each iteration for policy session. - * If this is the first write to NV then the NV_WRITTEN bit will get - * set and name needs re-computed */ - rc = wolfTPM2_NVOpen(dev, nv, nvIndex, NULL, 0); - if (rc != 0) - break; + /* Refresh for policy sessions and until NV_WRITTEN is cached. */ + if (!nv->handle.nameLoaded || + nv->handle.hndl != (TPM_HANDLE)nvIndex || + (nv->attributes & TPMA_NV_WRITTEN) == 0 || + (tpmSession != NULL && + TPM2_IS_POLICY_SESSION(tpmSession->handle.hndl))) { + rc = wolfTPM2_NVOpen(dev, nv, nvIndex, NULL, 0); + if (rc != 0) + break; + } /* For policy session recompute PCR for each iteration */ if (tpmSession != NULL && TPM2_IS_POLICY_SESSION(tpmSession->handle.hndl)) { diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 8dd121176..e6ceb9c7d 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1038,6 +1038,103 @@ static void test_wolfTPM2_BoundOwnEntity_ParamEnc(void) #endif } +/* Multi-chunk NV write plus rewrite under an HMAC parameter encryption + * session; a stale cached NV index name would fail the session HMAC. */ +static void test_wolfTPM2_NVWriteChunked(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(WOLFTPM_WINAPI) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_SESSION session; + WOLFTPM2_NV nv; + WOLFTPM2_HANDLE parent; + const word32 nvIndex = TPM2_DEMO_NV_TEST_CHUNKED_INDEX; + const byte nvAuth[] = "chunkedwriteauth"; + word32 nvAttributes; + /* 3 chunks; under the 1664 byte SLB9670 NV index max */ + byte buf[MAX_NV_BUFFER_SIZE*2 + 64]; + byte readBuf[sizeof(buf)]; + word32 readSz; + word32 i; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&session, 0, sizeof(session)); + XMEMSET(&nv, 0, sizeof(nv)); + XMEMSET(&parent, 0, sizeof(parent)); + for (i = 0; i < (word32)sizeof(buf); i++) { + buf[i] = (byte)(i & 0xFF); + } + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc != 0) { + printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n"); + return; + } + + parent.hndl = TPM_RH_OWNER; + rc = wolfTPM2_GetNvAttributesTemplate(parent.hndl, &nvAttributes); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = wolfTPM2_NVCreateAuth(&dev, &parent, &nv, nvIndex, nvAttributes, + (word32)sizeof(buf), nvAuth, (int)sizeof(nvAuth)-1); + if (rc == TPM_RC_NV_DEFINED) { + wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex); + XMEMSET(&nv, 0, sizeof(nv)); + rc = wolfTPM2_NVCreateAuth(&dev, &parent, &nv, nvIndex, nvAttributes, + (word32)sizeof(buf), nvAuth, (int)sizeof(nvAuth)-1); + } + if (rc != 0) { + /* NV limits vary by device. */ + wolfTPM2_Cleanup(&dev); + printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n"); + return; + } + + /* The parameter session HMAC binds the NV index name. */ + rc = wolfTPM2_StartSession(&dev, &session, NULL, NULL, TPM_SE_HMAC, + TPM_ALG_CFB); + AssertIntEQ(rc, TPM_RC_SUCCESS); + rc = wolfTPM2_SetAuthSession(&dev, 1, &session, + (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | + TPMA_SESSION_continueSession)); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle); + + /* First write sets TPMA_NV_WRITTEN. */ + rc = wolfTPM2_NVWriteAuth(&dev, &nv, nvIndex, buf, (word32)sizeof(buf), 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + readSz = (word32)sizeof(readBuf); + rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, readBuf, &readSz, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ((int)readSz, (int)sizeof(buf)); + AssertIntEQ(XMEMCMP(readBuf, buf, sizeof(buf)), 0); + + /* Rewrite uses the stable name. */ + for (i = 0; i < (word32)sizeof(buf); i++) { + buf[i] = (byte)(~i & 0xFF); + } + rc = wolfTPM2_NVWriteAuth(&dev, &nv, nvIndex, buf, (word32)sizeof(buf), 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + XMEMSET(readBuf, 0, sizeof(readBuf)); + readSz = (word32)sizeof(readBuf); + rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, readBuf, &readSz, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ((int)readSz, (int)sizeof(buf)); + AssertIntEQ(XMEMCMP(readBuf, buf, sizeof(buf)), 0); + + wolfTPM2_SetAuthSession(&dev, 1, NULL, 0); + wolfTPM2_UnloadHandle(&dev, &session.handle); + wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle); + wolfTPM2_NVDeleteAuth(&dev, &parent, nvIndex); + wolfTPM2_Cleanup(&dev); + printf("Test TPM Wrapper:\tNV write chunked:\tPassed\n"); +#else + printf("Test TPM Wrapper:\tNV write chunked:\tSkipped\n"); +#endif +} + static void test_wolfTPM2_PolicyHash(void) { #ifndef WOLFTPM2_NO_WOLFCRYPT @@ -7389,6 +7486,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc(); test_wolfTPM2_CreateLoaded_ParamEnc(); test_wolfTPM2_BoundOwnEntity_ParamEnc(); + test_wolfTPM2_NVWriteChunked(); test_wolfTPM2_PolicyHash(); test_wolfTPM2_SensitiveToPrivate(); test_TPM2_KDFa(); From b7dc3ba92e28c2e3a7aaad049bec52d600f4ae2d Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 4 Aug 2026 11:37:35 -0700 Subject: [PATCH 2/5] F-7607 - Grow crypto callback hash cache geometrically --- src/tpm2_cryptocb.c | 8 ++++- tests/unit_tests.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/tpm2_cryptocb.c b/src/tpm2_cryptocb.c index 0a6500e67..ac828e88b 100644 --- a/src/tpm2_cryptocb.c +++ b/src/tpm2_cryptocb.c @@ -815,13 +815,19 @@ static int wolfTPM2_HashUpdateCache(WOLFTPM2_HASHCTX* hashCtx, else if ((hashCtx->cacheSz + inSz) > hashCtx->cacheBufSz) { byte* oldIn = hashCtx->cacheBuf; word32 oldBufSz = hashCtx->cacheBufSz; + word32 newSz; /* check for overflow, including the block round-up below */ if (hashCtx->cacheSz + inSz < hashCtx->cacheSz || hashCtx->cacheSz + inSz > 0xFFFFFFFFU - (WOLFTPM2_HASH_BLOCK_SZ - 1)) { return BUFFER_E; } - hashCtx->cacheBufSz = (hashCtx->cacheSz + inSz + + newSz = hashCtx->cacheSz + inSz; + /* Block alignment keeps the round-up safe after doubling. */ + if (oldBufSz <= 0xFFFFFFFFU / 2 && (oldBufSz * 2) > newSz) { + newSz = oldBufSz * 2; + } + hashCtx->cacheBufSz = (newSz + WOLFTPM2_HASH_BLOCK_SZ - 1) & ~(WOLFTPM2_HASH_BLOCK_SZ - 1); hashCtx->cacheBuf = (byte*)XMALLOC(hashCtx->cacheBufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index e6ceb9c7d..f99aec9f5 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -4949,6 +4949,79 @@ static void test_wolfTPM2_CSR(void) #endif } +/* Exercise hash cache growth with small updates and verify the digest. */ +static void test_wolfTPM2_CryptoDevCb_HashCacheStream(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM_USE_SYMMETRIC) && \ + defined(WOLFSSL_HASH_FLAGS) && !defined(NO_SHA256) + int rc; + WOLFTPM2_DEV dev; + TpmCryptoDevCtx tpmCtx; + int tpmDevId = INVALID_DEVID; + wc_Sha256 sha; + byte digest[TPM_SHA256_DIGEST_SIZE]; + byte digestSw[TPM_SHA256_DIGEST_SIZE]; + byte data[4096]; + word32 i, pos, chunk; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&tpmCtx, 0, sizeof(tpmCtx)); + for (i = 0; i < (word32)sizeof(data); i++) { + data[i] = (byte)(i * 7 + 1); + } + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + if (rc != 0) { + printf("Test TPM Wrapper: %-40s Skipped\n", "CryptoDevCb hash cache:"); + return; + } + + tpmCtx.dev = &dev; + tpmCtx.useSymmetricOnTPM = 1; + rc = wolfTPM2_SetCryptoDevCb(&dev, wolfTPM2_CryptoDevCb, &tpmCtx, + &tpmDevId); + AssertIntEQ(rc, 0); + + /* Force repeated growth, then a greater-than-2x allocation. */ + rc = wc_InitSha256_ex(&sha, NULL, tpmDevId); + AssertIntEQ(rc, 0); + rc = wc_Sha256SetFlags(&sha, WC_HASH_FLAG_WILLCOPY); + AssertIntEQ(rc, 0); + pos = 0; + chunk = 1; + while (pos < (word32)sizeof(data) - 2048) { + if (chunk > (word32)sizeof(data) - 2048 - pos) + chunk = (word32)sizeof(data) - 2048 - pos; + rc = wc_Sha256Update(&sha, &data[pos], chunk); + AssertIntEQ(rc, 0); + pos += chunk; + chunk = (chunk % 96) + 1; + } + rc = wc_Sha256Update(&sha, &data[pos], 2048); + AssertIntEQ(rc, 0); + rc = wc_Sha256Final(&sha, digest); + AssertIntEQ(rc, 0); + wc_Sha256Free(&sha); + + rc = wc_InitSha256_ex(&sha, NULL, INVALID_DEVID); + AssertIntEQ(rc, 0); + rc = wc_Sha256Update(&sha, data, (word32)sizeof(data)); + AssertIntEQ(rc, 0); + rc = wc_Sha256Final(&sha, digestSw); + AssertIntEQ(rc, 0); + wc_Sha256Free(&sha); + + AssertIntEQ(XMEMCMP(digest, digestSw, sizeof(digest)), 0); + + wolfTPM2_ClearCryptoDevCb(&dev, tpmDevId); + wolfTPM2_Cleanup(&dev); + printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb hash cache:"); +#else + printf("Test TPM Wrapper: %-40s Skipped\n", "CryptoDevCb hash cache:"); +#endif +} + static void test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(void) { #if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \ @@ -7567,6 +7640,7 @@ int unit_tests(int argc, char *argv[]) test_GetAlgId(); test_wolfTPM2_ReadPublicKey(); test_wolfTPM2_CSR(); + test_wolfTPM2_CryptoDevCb_HashCacheStream(); test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(); test_wolfTPM2_CryptoDevCb_MlDsaSign(); test_TPM2_ASN_DecodeX509Cert_Errors(); From 7fd8b6c815f2aaf93c91a32de9d4adae8302eaea Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 4 Aug 2026 11:38:35 -0700 Subject: [PATCH 3/5] F-7608 - Keep Linux SPI device open across TIS transfers --- hal/tpm_io_linux.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/hal/tpm_io_linux.c b/hal/tpm_io_linux.c index 6f969577e..6c8044548 100644 --- a/hal/tpm_io_linux.c +++ b/hal/tpm_io_linux.c @@ -112,6 +112,7 @@ #define TPM2_SPI_DEV TPM2_SPI_DEV_PATH TPM2_SPI_DEV_CS static int spiOpenFailed = 0; #endif + static int spiDevFd = -1; #endif #endif @@ -262,7 +263,6 @@ word16 xferSz, void* userCtx) { int ret; - int spiDev; #ifdef WOLFTPM_CHECK_WAIT_STATE int timeout; #endif @@ -272,7 +272,6 @@ /* Note: PI has issue with 5-10Mhz on packets sized over 130 bytes */ unsigned int maxSpeed = TPM2_SPI_HZ; - int mode = 0; /* Mode 0 (CPOL=0, CPHA=0) */ int bits_per_word = 8; /* 8-bits */ #ifdef WOLFTPM_AUTODETECT @@ -288,16 +287,20 @@ #ifdef WOLFTPM_CHECK_WAIT_STATE timeout = TPM_SPI_WAIT_RETRY; #endif - spiDev = open(TPM2_SPI_DEV, O_RDWR); - if (spiDev >= 0) { + if (spiDevFd < 0) { + spiDevFd = open(TPM2_SPI_DEV, O_RDWR | O_CLOEXEC); + if (spiDevFd >= 0) { + int mode = 0; /* Mode 0 (CPOL=0, CPHA=0) */ + ioctl(spiDevFd, SPI_IOC_WR_MODE, &mode); + } + } + if (spiDevFd >= 0) { struct spi_ioc_transfer spi; size_t size; - ioctl(spiDev, SPI_IOC_WR_MODE, &mode); - ioctl(spiDev, SPI_IOC_WR_MAX_SPEED_HZ, &maxSpeed); - ioctl(spiDev, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word); - XMEMSET(&spi, 0, sizeof(spi)); + spi.speed_hz = maxSpeed; + spi.bits_per_word = bits_per_word; #ifdef WOLFTPM_CHECK_WAIT_STATE /* Keep CS asserted for header and flow control transfers */ @@ -307,7 +310,7 @@ spi.tx_buf = (unsigned long)txBuf; spi.rx_buf = (unsigned long)rxBuf; spi.len = TPM_TIS_HEADER_SZ; - size = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spi); + size = ioctl(spiDevFd, SPI_IOC_MESSAGE(1), &spi); if (size != TPM_TIS_HEADER_SZ) { ret = TPM_RC_FAILURE; } @@ -320,7 +323,7 @@ spi.len = 1; do { /* Check for SPI ready */ - size = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spi); + size = ioctl(spiDevFd, SPI_IOC_MESSAGE(1), &spi); } while ( (size == 1) && ((rxBuf[TPM_TIS_HEADER_SZ-1] & TPM_TIS_READY_MASK) == 0) && @@ -340,7 +343,7 @@ spi.tx_buf = (unsigned long)&txBuf[TPM_TIS_HEADER_SZ]; spi.rx_buf = (unsigned long)&rxBuf[TPM_TIS_HEADER_SZ]; spi.len = xferSz - TPM_TIS_HEADER_SZ; - size = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spi); + size = ioctl(spiDevFd, SPI_IOC_MESSAGE(1), &spi); if (size != (size_t)xferSz - TPM_TIS_HEADER_SZ) ret = TPM_RC_FAILURE; } @@ -349,7 +352,7 @@ if (spi.cs_change == 1) { spi.cs_change = 0; spi.len = 1; - size = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spi); + size = ioctl(spiDevFd, SPI_IOC_MESSAGE(1), &spi); (void)size; /* Ignore result */ } #else @@ -357,12 +360,15 @@ spi.tx_buf = (unsigned long)txBuf; spi.rx_buf = (unsigned long)rxBuf; spi.len = xferSz; - size = ioctl(spiDev, SPI_IOC_MESSAGE(1), &spi); + size = ioctl(spiDevFd, SPI_IOC_MESSAGE(1), &spi); if (size != (size_t)xferSz) ret = TPM_RC_FAILURE; #endif /* WOLFTPM_CHECK_WAIT_STATE */ - close(spiDev); + if (ret != TPM_RC_SUCCESS) { + close(spiDevFd); + spiDevFd = -1; + } } else { /* Failed to open device */ @@ -396,6 +402,10 @@ foundSpiDev = 1; } else { + if (spiDevFd >= 0) { + close(spiDevFd); + spiDevFd = -1; + } devLen = (int)XSTRLEN(TPM2_SPI_DEV); /* tries spidev0.[0-4] */ if (TPM2_SPI_DEV[devLen-1] < MAX_SPI_DEV_CS) { From f6b364f7b8734ff1f1efc66202f2de33a8f96a38 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 4 Aug 2026 11:38:35 -0700 Subject: [PATCH 4/5] F-7609 - Keep swtpm TCP connection open across commands --- docs/SWTPM.md | 2 +- src/fwtpm/fwtpm_io.c | 82 ++++++++++++++++++++++++++------------------ src/tpm2.c | 14 ++++---- src/tpm2_swtpm.c | 43 ++++++++++++++++++----- src/tpm2_wrap.c | 14 ++++++++ tests/unit_tests.c | 1 + wolftpm/tpm2_swtpm.h | 6 ++-- 7 files changed, 108 insertions(+), 54 deletions(-) diff --git a/docs/SWTPM.md b/docs/SWTPM.md index e2b1f9f05..f66ee0ab6 100644 --- a/docs/SWTPM.md +++ b/docs/SWTPM.md @@ -51,7 +51,7 @@ Build Options: * `TPM2_SWTPM_HOST`: The serial device path (default is `/dev/ttyACM0` on Linux, `/dev/cu.usbmodem` on macOS). Can be overridden at runtime via the `TPM2_SWTPM_HOST` environment variable. * `TPM2_SWTPM_PORT`: The baud rate (default is 115200) -The UART transport uses the same mssim protocol as the socket transport. The serial port is configured as 8N1 raw mode with no flow control. Unlike the socket transport, the serial port file descriptor is kept open across commands (no reconnect per command). +The UART transport uses the same mssim protocol as the socket transport. The serial port is configured as 8N1 raw mode with no flow control. Like the socket transport, the serial port file descriptor is kept open across commands (no reconnect per command). Both transports close the connection during `wolfTPM2_Cleanup`. On the socket transport any transmit/receive failure also closes the connection so the next command reconnects; the UART transport closes only when the per-command `TPM_SESSION_END` write fails. #### Security note: environment variable override diff --git a/src/fwtpm/fwtpm_io.c b/src/fwtpm/fwtpm_io.c index e2d4fdbe8..fb16ab1c4 100644 --- a/src/fwtpm/fwtpm_io.c +++ b/src/fwtpm/fwtpm_io.c @@ -84,6 +84,8 @@ int FWTPM_IO_IsStopRequested(void) #ifndef WOLFTPM_FWTPM_TIS /* --- Low-level socket helpers --- */ +#define FWTPM_MAX_COMMAND_CLIENTS 8 + static int SocketSend(SOCKET_T fd, const void* buf, int sz) { const char* ptr = (const char*)buf; @@ -650,10 +652,11 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) int rc = TPM_RC_SUCCESS; fd_set readFds; int maxFd; - SOCKET_T cmdFd = FWTPM_INVALID_FD; /* active command client fd */ + SOCKET_T cmdFds[FWTPM_MAX_COMMAND_CLIENTS]; SOCKET_T platFd = FWTPM_INVALID_FD; /* active platform client fd */ struct timeval tv; int selRc; + int i; #ifndef _WIN32 struct sigaction sa; #endif @@ -667,6 +670,9 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) return FWTPM_TIS_ServerLoop(ctx); #else ctx->running = 1; + for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { + cmdFds[i] = FWTPM_INVALID_FD; + } #ifndef _WIN32 /* Ignore SIGPIPE so write to closed socket returns error instead @@ -697,9 +703,13 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) maxFd = ctx->io.platListenFd; /* Watch active client connections for incoming data */ - if (cmdFd != FWTPM_INVALID_FD) { - FD_SET(cmdFd, &readFds); - if (cmdFd > maxFd) maxFd = cmdFd; + for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { + if (cmdFds[i] != FWTPM_INVALID_FD) { + FD_SET(cmdFds[i], &readFds); + if (cmdFds[i] > maxFd) { + maxFd = cmdFds[i]; + } + } } if (platFd != FWTPM_INVALID_FD) { FD_SET(platFd, &readFds); @@ -739,48 +749,54 @@ int FWTPM_IO_ServerLoop(FWTPM_CTX* ctx) } } + /* Handle one message from active platform client */ + if (platFd != FWTPM_INVALID_FD && FD_ISSET(platFd, &readFds)) { + if (HandlePlatformCommand(ctx, platFd) != TPM_RC_SUCCESS) { + CloseSocket(platFd); + platFd = FWTPM_INVALID_FD; + } + } + + /* Handle one message from each ready command client */ + for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { + if (cmdFds[i] != FWTPM_INVALID_FD && + FD_ISSET(cmdFds[i], &readFds)) { + if (HandleCommandConnection(ctx, cmdFds[i]) != + TPM_RC_SUCCESS) { + CloseSocket(cmdFds[i]); + cmdFds[i] = FWTPM_INVALID_FD; + } + } + } + /* Accept new command connection */ if (FD_ISSET(ctx->io.listenFd, &readFds)) { SOCKET_T newFd = accept(ctx->io.listenFd, NULL, NULL); if (newFd != FWTPM_INVALID_FD) { - if (cmdFd != FWTPM_INVALID_FD) { - /* Consume any select-confirmed in-flight command on the - * old connection before dropping it, so a pending - * request is not silently lost. */ - if (FD_ISSET(cmdFd, &readFds)) { - HandleCommandConnection(ctx, cmdFd); + for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { + if (cmdFds[i] == FWTPM_INVALID_FD) { + cmdFds[i] = newFd; + break; } + } + if (i == FWTPM_MAX_COMMAND_CLIENTS) { #ifdef DEBUG_WOLFTPM - printf("fwTPM: command connection replaced\n"); + printf("fwTPM: too many command connections\n"); #endif - CloseSocket(cmdFd); + CloseSocket(newFd); } - cmdFd = newFd; - } - } - - /* Handle one message from active platform client */ - if (platFd != FWTPM_INVALID_FD && FD_ISSET(platFd, &readFds)) { - if (HandlePlatformCommand(ctx, platFd) != TPM_RC_SUCCESS) { - CloseSocket(platFd); - platFd = FWTPM_INVALID_FD; } } + } - /* Handle one message from active command client */ - if (cmdFd != FWTPM_INVALID_FD && FD_ISSET(cmdFd, &readFds)) { - if (HandleCommandConnection(ctx, cmdFd) != TPM_RC_SUCCESS) { - CloseSocket(cmdFd); - cmdFd = FWTPM_INVALID_FD; - /* Transient state persists across command connections: the - * mssim transport reconnects per command for one logical TPM, - * so a clean disconnect must not flush handles. */ - } + for (i = 0; i < FWTPM_MAX_COMMAND_CLIENTS; i++) { + if (cmdFds[i] != FWTPM_INVALID_FD) { + CloseSocket(cmdFds[i]); } } - - if (cmdFd != FWTPM_INVALID_FD) CloseSocket(cmdFd); - if (platFd != FWTPM_INVALID_FD) CloseSocket(platFd); + if (platFd != FWTPM_INVALID_FD) { + CloseSocket(platFd); + } return rc; #endif /* !WOLFTPM_FWTPM_TIS */ diff --git a/src/tpm2.c b/src/tpm2.c index 6c4c8cee5..40989058d 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -836,6 +836,11 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, XMEMSET(ctx, 0, sizeof(TPM2_CTX)); +#if defined(WOLFTPM_SWTPM) + /* set before any early return so cleanup cannot act on fd 0 */ + ctx->tcpCtx.fd = -1; +#endif + #ifndef WOLFTPM_NO_RETRY ctx->retries = WOLFTPM_MAX_RETRIES; #endif @@ -846,10 +851,6 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, return rc; #endif -#if defined(WOLFTPM_SWTPM) - ctx->tcpCtx.fd = -1; -#endif - #if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \ defined(WOLFTPM_WINAPI) if (ioCb != NULL || userCtx != NULL) { @@ -958,9 +959,8 @@ TPM_RC TPM2_Cleanup(TPM2_CTX* ctx) close(ctx->fd); #endif -#ifdef WOLFTPM_SWTPM_UART - /* Close the persistent UART connection */ - TPM2_SwtpmCloseUART(ctx); +#ifdef WOLFTPM_SWTPM + TPM2_SwtpmClose(ctx); #endif return TPM_RC_SUCCESS; diff --git a/src/tpm2_swtpm.c b/src/tpm2_swtpm.c index 9293e23bd..34c1c64b5 100644 --- a/src/tpm2_swtpm.c +++ b/src/tpm2_swtpm.c @@ -56,7 +56,9 @@ #include #include #include +#include #include +#include #endif #if !defined(NO_GETENV) || defined(WOLFTPM_SWTPM_UART) #include /* getenv / atoi */ @@ -117,7 +119,13 @@ static TPM_RC SwTpmTransmit(TPM2_CTX* ctx, const void* buffer, ssize_t bufSz) ptr = (const char*)buffer; remaining = bufSz; while (remaining > 0) { + #if !defined(WOLFTPM_SWTPM_UART) && !defined(WOLFTPM_ZEPHYR) && \ + defined(MSG_NOSIGNAL) + /* a dead peer must return an error, not raise SIGPIPE */ + wrc = send(ctx->tcpCtx.fd, ptr, remaining, MSG_NOSIGNAL); + #else wrc = write(ctx->tcpCtx.fd, ptr, remaining); + #endif if (wrc < 0) { /* Retry on EINTR (signal). EAGAIN/EWOULDBLOCK shouldn't normally * happen on the default blocking fd, but treat them as transient. */ @@ -402,6 +410,7 @@ static TPM_RC SwTpmConnect(TPM2_CTX* ctx, const char* host, const char* port) #endif #else /* !WOLFTPM_ZEPHYR */ int s; + int sockOpt = 1; struct addrinfo hints; struct addrinfo *result, *rp; @@ -434,6 +443,18 @@ static TPM_RC SwTpmConnect(TPM2_CTX* ctx, const char* host, const char* port) freeaddrinfo(result); if (rp != NULL) { + (void)sockOpt; + #ifdef FD_CLOEXEC + (void)fcntl(fd, F_SETFD, FD_CLOEXEC); + #endif + #ifdef SO_NOSIGPIPE + (void)setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &sockOpt, + sizeof(sockOpt)); + #endif + #ifdef TCP_NODELAY + (void)setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &sockOpt, + sizeof(sockOpt)); + #endif ctx->tcpCtx.fd = fd; rc = TPM_RC_SUCCESS; } @@ -466,11 +487,7 @@ static TPM_RC SwTpmDisconnect(TPM2_CTX* ctx) #endif #ifdef WOLFTPM_SWTPM_UART - /* UART: on success, 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. - * On SESSION_END write failure, close and reset the fd so the next - * command reconnects instead of reusing a broken connection. */ + /* Keep the port open unless SESSION_END fails. */ if (rc != TPM_RC_SUCCESS) { close(ctx->tcpCtx.fd); ctx->tcpCtx.fd = -1; @@ -609,24 +626,32 @@ int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) } #endif +#ifdef WOLFTPM_SWTPM_UART if (ctx->tcpCtx.fd >= 0) { TPM_RC rc_disconnect = SwTpmDisconnect(ctx); if (rc == TPM_RC_SUCCESS) { rc = rc_disconnect; } } +#else + /* Reconnect after a transport failure. */ + if (rc != TPM_RC_SUCCESS && ctx->tcpCtx.fd >= 0) { + (void)SwTpmDisconnect(ctx); + } +#endif return rc; } -#ifdef WOLFTPM_SWTPM_UART -/* Close the persistent UART FD during final TPM context cleanup */ -void TPM2_SwtpmCloseUART(TPM2_CTX* ctx) +void TPM2_SwtpmClose(TPM2_CTX* ctx) { if (ctx != NULL && ctx->tcpCtx.fd >= 0) { + #ifdef WOLFTPM_SWTPM_UART close(ctx->tcpCtx.fd); ctx->tcpCtx.fd = -1; + #else + (void)SwTpmDisconnect(ctx); + #endif } } -#endif #endif /* WOLFTPM_SWTPM */ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 6de0d5383..dd12e98fd 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -29,6 +29,9 @@ #ifdef WOLFTPM_SPDM #include #endif +#ifdef WOLFTPM_SWTPM +#include +#endif /* Convert big-endian byte array to native word32 */ word32 wolfTPM2_RsaKey_Exponent(const byte* e, word32 eSz) @@ -175,6 +178,9 @@ static int wolfTPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, #ifdef DEBUG_WOLFTPM printf("TPM2_Startup failed %d: %s\n", rc, wolfTPM2_GetRCString(rc)); + #endif + #ifdef WOLFTPM_SWTPM + TPM2_SwtpmClose(ctx); #endif return rc; } @@ -213,6 +219,9 @@ static int wolfTPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, #ifdef DEBUG_WOLFTPM printf("TPM2_SelfTest failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); + #endif + #ifdef WOLFTPM_SWTPM + TPM2_SwtpmClose(ctx); #endif return rc; } @@ -243,6 +252,11 @@ int wolfTPM2_Test(TPM2HalIoCb ioCb, void* userCtx, WOLFTPM2_CAPS* caps) /* Perform startup and test device */ rc = wolfTPM2_Init_ex(&ctx, ioCb, userCtx, TPM_STARTUP_TEST_TRIES); if (rc != TPM_RC_SUCCESS) { + #ifdef WOLFTPM_SWTPM + TPM2_SwtpmClose(&ctx); + #endif + /* Restore the active context before ctx leaves scope. */ + TPM2_SetActiveCtx(current_ctx); return rc; } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index f99aec9f5..7a87f9ae7 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -144,6 +144,7 @@ static void test_wolfTPM2_Init(void) defined(WOLFTPM_WINAPI) /* Custom IO Callbacks are not needed for Linux TIS driver */ AssertIntEQ(rc, 0); + wolfTPM2_Cleanup(&dev); #else /* IO Callbacks are required for SPIdev/I2C and must be valid */ AssertIntNE(rc, 0); diff --git a/wolftpm/tpm2_swtpm.h b/wolftpm/tpm2_swtpm.h index 982c5fbd6..6b301a945 100644 --- a/wolftpm/tpm2_swtpm.h +++ b/wolftpm/tpm2_swtpm.h @@ -50,10 +50,8 @@ WOLFTPM_LOCAL int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet); /* Validate a swtpm-reported response size against the receive buffer size */ WOLFTPM_API int TPM2_SwtpmValidateRspSz(int packetSize, uint32_t rspSz); -#ifdef WOLFTPM_SWTPM_UART -/* Close the persistent UART FD during final TPM context cleanup */ -WOLFTPM_LOCAL void TPM2_SwtpmCloseUART(TPM2_CTX* ctx); -#endif +/* Close the persistent UART or socket FD during final TPM context cleanup */ +WOLFTPM_LOCAL void TPM2_SwtpmClose(TPM2_CTX* ctx); #ifdef __cplusplus } /* extern "C" */ From 8030099dc39e45190485a1d5da669b802a9588c2 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 4 Aug 2026 11:38:35 -0700 Subject: [PATCH 5/5] F-7610 - Keep Linux I2C device open across TIS transfers --- hal/tpm_io_linux.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hal/tpm_io_linux.c b/hal/tpm_io_linux.c index 6c8044548..1f02978d2 100644 --- a/hal/tpm_io_linux.c +++ b/hal/tpm_io_linux.c @@ -76,6 +76,7 @@ #define TPM2_I2C_DEV "/dev/i2c-1" #define TPM2_I2C_HZ 400000 /* 400kHz */ static int i2cOpenFailed = 0; + static int i2cDevFd = -1; #else /* SPI */ #ifndef TPM2_SPI_DEV_CS @@ -194,14 +195,19 @@ word16 size, void* userCtx) { int ret = TPM_RC_FAILURE; - int i2cDev = open(TPM2_I2C_DEV, O_RDWR); - if (i2cDev >= 0) { + if (i2cDevFd < 0) { + i2cDevFd = open(TPM2_I2C_DEV, O_RDWR | O_CLOEXEC); + } + if (i2cDevFd >= 0) { if (isRead) - ret = i2c_read(i2cDev, addr, buf, size); + ret = i2c_read(i2cDevFd, addr, buf, size); else - ret = i2c_write(i2cDev, addr, buf, size); + ret = i2c_write(i2cDevFd, addr, buf, size); - close(i2cDev); + if (ret != TPM_RC_SUCCESS) { + close(i2cDevFd); + i2cDevFd = -1; + } } else if (!i2cOpenFailed) { i2cOpenFailed = 1;