Keep fixed fwTPM TIS semaphore names and protocol version

pull/588/head
Aidan Garske 2026-09-02 12:44:45 -07:00
parent a7b1aa6c3b
commit 18d9ead84f
5 changed files with 29 additions and 158 deletions

View File

@ -765,18 +765,13 @@ register-level access. This mode simulates an SPI-attached TPM.
| Define | Default | Description |
|--------|---------|-------------|
| `FWTPM_TIS_SHM_PATH` | `/tmp/fwtpm.shm` | Shared memory file; clients require a regular, single-link, same-UID, exact-size `0600` endpoint |
| `FWTPM_TIS_SEM_CMD` | `/fwtpm_cmd` | Command semaphore prefix; a per-UID suffix is always appended |
| `FWTPM_TIS_SEM_RSP` | `/fwtpm_rsp` | Response semaphore prefix; a per-UID suffix is always appended |
| `FWTPM_TIS_SEM_CMD` | `/fwtpm_cmd` | Command semaphore name |
| `FWTPM_TIS_SEM_RSP` | `/fwtpm_rsp` | Response semaphore name |
Protocol version 2 requires an exact version and shared-region-size match.
Rebuild the client library and `fwtpm_server` together when upgrading or when
changing options that affect `FWTPM_TIS_FIFO_SIZE`; version 1 and version 2
peers do not interoperate.
The default shared-memory path is global, so it supports one server per host.
The per-UID semaphore suffix enables safe stale-object cleanup; it does not
enable concurrent per-user servers unless each build also uses a distinct
`FWTPM_TIS_SHM_PATH`.
Clients require an exact protocol version and shared-region-size match, so
rebuild the client library and `fwtpm_server` together when changing options
that affect `FWTPM_TIS_FIFO_SIZE`. The default paths are global, so one server
per host.
**Server-side API:**

View File

@ -84,24 +84,6 @@ static int FWTPM_TIS_ServerActive(const FWTPM_TIS_REGS* shm)
return FWTPM_TIS_ATOMIC_LOAD(shm->magic) == FWTPM_TIS_MAGIC;
}
/* Must match the server's naming in fwtpm_tis_shm.c. */
static int FWTPM_TIS_ClientMakeSemNames(uid_t ownerUid, char* semCmd,
size_t semCmdSz, char* semRsp, size_t semRspSz)
{
int cmdLen;
int rspLen;
cmdLen = XSNPRINTF(semCmd, semCmdSz, "%s-%lu", FWTPM_TIS_SEM_CMD,
(unsigned long)ownerUid);
rspLen = XSNPRINTF(semRsp, semRspSz, "%s-%lu", FWTPM_TIS_SEM_RSP,
(unsigned long)ownerUid);
if (cmdLen <= 0 || (size_t)cmdLen >= semCmdSz ||
rspLen <= 0 || (size_t)rspLen >= semRspSz) {
return -1;
}
return 0;
}
static int FWTPM_TIS_ClientValidateShm(const struct stat* st)
{
mode_t expectedMode = S_IRUSR | S_IWUSR;
@ -126,8 +108,6 @@ int FWTPM_TIS_ClientConnect(FWTPM_TIS_CLIENT_CTX* client)
FWTPM_TIS_REGS* shm;
sem_t* semCmd;
sem_t* semRsp;
char semCmdName[FWTPM_TIS_SEM_NAME_SIZE];
char semRspName[FWTPM_TIS_SEM_NAME_SIZE];
if (client == NULL) {
return BAD_FUNC_ARG;
@ -226,15 +206,6 @@ int FWTPM_TIS_ClientConnect(FWTPM_TIS_CLIENT_CTX* client)
return TPM_RC_FAILURE;
}
#endif
if (FWTPM_TIS_ClientMakeSemNames(st.st_uid, semCmdName,
sizeof(semCmdName), semRspName, sizeof(semRspName)) != 0) {
#ifdef DEBUG_WOLFTPM
printf("fwTPM HAL: failed to derive semaphore names for uid %lu\n",
(unsigned long)st.st_uid);
#endif
close(fd);
return TPM_RC_FAILURE;
}
shm = (FWTPM_TIS_REGS*)mmap(NULL, sizeof(FWTPM_TIS_REGS),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
@ -260,22 +231,22 @@ int FWTPM_TIS_ClientConnect(FWTPM_TIS_CLIENT_CTX* client)
}
/* Open existing semaphores (server creates them) */
semCmd = sem_open(semCmdName, 0);
semCmd = sem_open(FWTPM_TIS_SEM_CMD, 0);
if (semCmd == SEM_FAILED) {
#ifdef DEBUG_WOLFTPM
printf("fwTPM HAL: sem_open(%s) failed: %d (%s)\n",
semCmdName, errno, strerror(errno));
FWTPM_TIS_SEM_CMD, errno, strerror(errno));
#endif
munmap(shm, sizeof(FWTPM_TIS_REGS));
close(fd);
return TPM_RC_FAILURE;
}
semRsp = sem_open(semRspName, 0);
semRsp = sem_open(FWTPM_TIS_SEM_RSP, 0);
if (semRsp == SEM_FAILED) {
#ifdef DEBUG_WOLFTPM
printf("fwTPM HAL: sem_open(%s) failed: %d (%s)\n",
semRspName, errno, strerror(errno));
FWTPM_TIS_SEM_RSP, errno, strerror(errno));
#endif
sem_close(semCmd);
munmap(shm, sizeof(FWTPM_TIS_REGS));

View File

@ -58,38 +58,11 @@ typedef struct {
int shmFd; /* shm file descriptor */
sem_t* semCmd; /* command semaphore */
sem_t* semRsp; /* response semaphore */
char semCmdName[FWTPM_TIS_SEM_NAME_SIZE];
char semRspName[FWTPM_TIS_SEM_NAME_SIZE];
} FWTPM_TIS_SHM_CTX;
/* Single server per process */
static FWTPM_TIS_SHM_CTX gTisShmCtx;
static void TisShmUnlinkOldEndpoint(const char* semCmdName,
const char* semRspName)
{
(void)sem_unlink(semCmdName);
(void)sem_unlink(semRspName);
}
/* Per-UID names keep concurrent users' wakeup semaphores apart. */
static int TisShmMakeSemNames(uid_t ownerUid, char* semCmd, size_t semCmdSz,
char* semRsp, size_t semRspSz)
{
int cmdLen;
int rspLen;
cmdLen = XSNPRINTF(semCmd, semCmdSz, "%s-%lu", FWTPM_TIS_SEM_CMD,
(unsigned long)ownerUid);
rspLen = XSNPRINTF(semRsp, semRspSz, "%s-%lu", FWTPM_TIS_SEM_RSP,
(unsigned long)ownerUid);
if (cmdLen <= 0 || (size_t)cmdLen >= semCmdSz ||
rspLen <= 0 || (size_t)rspLen >= semRspSz) {
return -1;
}
return 0;
}
/* --- HAL Callbacks --- */
static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
@ -113,8 +86,6 @@ static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
shm->shmFd = -1;
shm->semCmd = NULL;
shm->semRsp = NULL;
shm->semCmdName[0] = '\0';
shm->semRspName[0] = '\0';
/* Use a fresh inode so locks from a crashed server generation do not
* prevent new clients from connecting to the replacement server. */
@ -149,24 +120,6 @@ static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
return -1;
}
if (fstat(fd, &shmStat) != 0 ||
shmStat.st_size != (off_t)sizeof(FWTPM_TIS_REGS) ||
(shmStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) !=
(S_IRUSR | S_IWUSR)) {
fprintf(stderr, "fwTPM TIS: invalid shm endpoint metadata\n");
close(fd);
(void)unlink(FWTPM_TIS_SHM_PATH);
return -1;
}
if (TisShmMakeSemNames(shmStat.st_uid,
shm->semCmdName, sizeof(shm->semCmdName),
shm->semRspName, sizeof(shm->semRspName)) != 0) {
fprintf(stderr, "fwTPM TIS: failed to derive semaphore names\n");
close(fd);
(void)unlink(FWTPM_TIS_SHM_PATH);
return -1;
}
shm->regs = (FWTPM_TIS_REGS*)mmap(NULL, sizeof(FWTPM_TIS_REGS),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm->regs == MAP_FAILED) {
@ -182,11 +135,12 @@ static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
/* Remove stale names only after the data endpoint is ready, immediately
* before O_EXCL recreates them. Early startup failures therefore leave a
* still-running server generation's semaphore names intact. */
TisShmUnlinkOldEndpoint(shm->semCmdName, shm->semRspName);
shm->semCmd = sem_open(shm->semCmdName, O_CREAT | O_EXCL, 0600, 0);
(void)sem_unlink(FWTPM_TIS_SEM_CMD);
(void)sem_unlink(FWTPM_TIS_SEM_RSP);
shm->semCmd = sem_open(FWTPM_TIS_SEM_CMD, O_CREAT | O_EXCL, 0600, 0);
if (shm->semCmd == SEM_FAILED) {
fprintf(stderr, "fwTPM TIS: sem_open(%s) failed: %d (%s)\n",
shm->semCmdName, errno, strerror(errno));
FWTPM_TIS_SEM_CMD, errno, strerror(errno));
shm->semCmd = NULL;
munmap(shm->regs, sizeof(FWTPM_TIS_REGS));
shm->regs = NULL;
@ -196,12 +150,12 @@ static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
return -1;
}
shm->semRsp = sem_open(shm->semRspName, O_CREAT | O_EXCL, 0600, 0);
shm->semRsp = sem_open(FWTPM_TIS_SEM_RSP, O_CREAT | O_EXCL, 0600, 0);
if (shm->semRsp == SEM_FAILED) {
fprintf(stderr, "fwTPM TIS: sem_open(%s) failed: %d (%s)\n",
shm->semRspName, errno, strerror(errno));
FWTPM_TIS_SEM_RSP, errno, strerror(errno));
sem_close(shm->semCmd);
sem_unlink(shm->semCmdName);
sem_unlink(FWTPM_TIS_SEM_CMD);
shm->semCmd = NULL;
shm->semRsp = NULL;
munmap(shm->regs, sizeof(FWTPM_TIS_REGS));
@ -217,7 +171,7 @@ static int TisShmInit(void* ctx, FWTPM_TIS_REGS** regs)
printf("fwTPM TIS: Shared memory at %s (%zu bytes)\n",
FWTPM_TIS_SHM_PATH, sizeof(FWTPM_TIS_REGS));
printf("fwTPM TIS: Semaphores: cmd=%s, rsp=%s\n",
shm->semCmdName, shm->semRspName);
FWTPM_TIS_SEM_CMD, FWTPM_TIS_SEM_RSP);
return 0;
}
@ -262,16 +216,12 @@ static void TisShmCleanup(void* ctx)
}
if (shm->semRsp != NULL && shm->semRsp != SEM_FAILED) {
sem_close(shm->semRsp);
if (shm->semRspName[0] != '\0') {
sem_unlink(shm->semRspName);
}
sem_unlink(FWTPM_TIS_SEM_RSP);
shm->semRsp = NULL;
}
if (shm->semCmd != NULL && shm->semCmd != SEM_FAILED) {
sem_close(shm->semCmd);
if (shm->semCmdName[0] != '\0') {
sem_unlink(shm->semCmdName);
}
sem_unlink(FWTPM_TIS_SEM_CMD);
shm->semCmd = NULL;
}
if (shm->regs != NULL) {

View File

@ -41,8 +41,8 @@
static char gTestShmPath[96];
static char gTestAuxPath[96];
static char gTestSemCmdBase[16];
static char gTestSemRspBase[16];
static char gTestSemCmd[16];
static char gTestSemRsp[16];
static int gSwapAfterSemOpen;
static int gSemOpenCount;
static int gSwapFailed;
@ -52,16 +52,13 @@ static int CreateEndpointFile(const char* path, mode_t mode,
static int ReplaceEndpoint(void);
#define FWTPM_TIS_SHM_PATH gTestShmPath
#define FWTPM_TIS_SEM_CMD gTestSemCmdBase
#define FWTPM_TIS_SEM_RSP gTestSemRspBase
#define FWTPM_TIS_SEM_CMD gTestSemCmd
#define FWTPM_TIS_SEM_RSP gTestSemRsp
#define WOLFTPM_INCLUDE_IO_FILE
#include "../hal/tpm_io.h"
#include <wolftpm/fwtpm/fwtpm_tis.h>
static char gTestSemCmd[FWTPM_TIS_SEM_NAME_SIZE];
static char gTestSemRsp[FWTPM_TIS_SEM_NAME_SIZE];
/* tpm_io_fwtpm.c normally gets this helper from libwolftpm. */
static void TestForceZero(void* mem, word32 len)
{
@ -165,9 +162,7 @@ static int CreateEndpoint(mode_t mode, off_t sizeAdjust, UINT32 magic,
int rc = -1;
CleanupEndpoint();
if (FWTPM_TIS_ClientMakeSemNames(geteuid(), gTestSemCmd,
sizeof(gTestSemCmd), gTestSemRsp, sizeof(gTestSemRsp)) != 0 ||
CreateEndpointFile(gTestShmPath, mode, sizeAdjust, magic,
if (CreateEndpointFile(gTestShmPath, mode, sizeAdjust, magic,
version) != 0) {
goto exit;
}
@ -426,40 +421,6 @@ static int TestMetadataValidation(void)
return 0;
}
static int TestCustomSemPrefixes(void)
{
char semCmd[FWTPM_TIS_SEM_NAME_SIZE];
char semRsp[FWTPM_TIS_SEM_NAME_SIZE];
char otherCmd[FWTPM_TIS_SEM_NAME_SIZE];
char otherRsp[FWTPM_TIS_SEM_NAME_SIZE];
char expectedCmd[FWTPM_TIS_SEM_NAME_SIZE];
char expectedRsp[FWTPM_TIS_SEM_NAME_SIZE];
uid_t ownerUid = geteuid();
(void)snprintf(expectedCmd, sizeof(expectedCmd), "%s-%lu",
gTestSemCmdBase, (unsigned long)ownerUid);
(void)snprintf(expectedRsp, sizeof(expectedRsp), "%s-%lu",
gTestSemRspBase, (unsigned long)ownerUid);
if (FWTPM_TIS_ClientMakeSemNames(ownerUid, semCmd, sizeof(semCmd),
semRsp, sizeof(semRsp)) != 0 ||
FWTPM_TIS_ClientMakeSemNames(ownerUid + 1U, otherCmd,
sizeof(otherCmd), otherRsp, sizeof(otherRsp)) != 0 ||
XSTRCMP(semCmd, expectedCmd) != 0 ||
XSTRCMP(semRsp, expectedRsp) != 0 ||
XSTRCMP(semCmd, otherCmd) == 0 ||
XSTRCMP(semRsp, otherRsp) == 0) {
printf("FAIL: caller semaphore prefixes lost UID namespacing\n");
return 1;
}
if (FWTPM_TIS_ClientMakeSemNames(ownerUid, semCmd, 2U, semRsp,
sizeof(semRsp)) == 0) {
printf("FAIL: accepted a truncated semaphore name\n");
return 1;
}
printf("PASS: namespaced caller-defined semaphore prefixes\n");
return 0;
}
int main(void)
{
int failures = 0;
@ -470,9 +431,9 @@ int main(void)
"/tmp/wolftpm-fwtpm-hal-%ld.shm", pid);
(void)snprintf(gTestAuxPath, sizeof(gTestAuxPath),
"/tmp/wolftpm-fwtpm-hal-%ld.aux", pid);
(void)snprintf(gTestSemCmdBase, sizeof(gTestSemCmdBase),
(void)snprintf(gTestSemCmd, sizeof(gTestSemCmd),
"/c%x", semId);
(void)snprintf(gTestSemRspBase, sizeof(gTestSemRspBase),
(void)snprintf(gTestSemRsp, sizeof(gTestSemRsp),
"/r%x", semId);
failures += ExpectRejected("group-readable", 0640, 0,
@ -494,7 +455,6 @@ int main(void)
failures += ExpectAccepted();
failures += ExpectDisconnectZeroized();
failures += TestMetadataValidation();
failures += TestCustomSemPrefixes();
CleanupEndpoint();
printf("fwTPM HAL endpoint tests: %s\n",

View File

@ -36,8 +36,7 @@
extern "C" {
#endif
/* Shared-memory path and semaphore-name prefixes (POSIX transport defaults).
* Every semaphore prefix receives a per-UID suffix. */
/* Shared memory and semaphore paths (POSIX transport defaults) */
#ifndef FWTPM_TIS_SHM_PATH
#define FWTPM_TIS_SHM_PATH "/tmp/fwtpm.shm"
#endif
@ -48,13 +47,9 @@
#define FWTPM_TIS_SEM_RSP "/fwtpm_rsp"
#endif
/* Holds a leading slash, the 251 characters Linux permits after it, and NUL.
* Shorter platform limits are enforced by sem_open(). */
#define FWTPM_TIS_SEM_NAME_SIZE 253
/* Magic and version for shared memory validation */
#define FWTPM_TIS_MAGIC 0x57544953UL /* "WTIS" */
#define FWTPM_TIS_VERSION 2
#define FWTPM_TIS_VERSION 1
/* Publish/observe the magic sentinel with release/acquire ordering so a client
* never sees FWTPM_TIS_MAGIC before the header it guards (wc_port.h ladder). */