From 93bd98c7e8cabc009569c60d3df96253f65fc93c Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Mon, 4 May 2026 19:00:52 +0300 Subject: [PATCH] swtpm: ctrlchannel: flush PERMANENT state before GET_STATEBLOB ctrlchannel_return_state() reads tpm2-00.permall from disk when handling GET_STATEBLOB(PERMANENT). At the same time libtpms holds the live PERMANENT state in RAM and only flushes it via _plat__NvCommit(). If, for whatever reason, the on-disk state blob is missing, empty or out of sync relative to libtpms RAM at the moment live migration runs, an empty or short PERMANENT blob is shipped to the destination, which results in: qemu-kvm: tpm-emulator: Setting the stateblob (type 2) failed with a TPM error 0x800 Regardless of how the desync arose (orchestrator-level cleanup of the state dir, transient NvCommit failure on a prior receive, etc.), let's add additional sync as a workaround for such cases. Namely, add SWTPM_NVRAM_Store_Permanent() that fetches PERMANENT from libtpms via TPMLIB_GetState() and writes it through SWTPM_NVRAM_StoreData() - similarly to how it's done for SWTPM_NVRAM_Store_Volatile(). That is to make sure disk is force-synced from RAM at the migration moment. Signed-off-by: Andrey Drobyshev --- src/swtpm/ctrlchannel.c | 3 +++ src/swtpm/swtpm_nvstore.c | 31 +++++++++++++++++++++++++++++++ src/swtpm/swtpm_nvstore.h | 1 + 3 files changed, 35 insertions(+) diff --git a/src/swtpm/ctrlchannel.c b/src/swtpm/ctrlchannel.c index 9be8dbd4..1d1c709d 100644 --- a/src/swtpm/ctrlchannel.c +++ b/src/swtpm/ctrlchannel.c @@ -175,6 +175,9 @@ static int ctrlchannel_return_state(ptm_getstate *pgs, int fd, if (!blobname) res = TPM_FAIL; + if (res == 0 && blobtype == PTM_BLOB_TYPE_PERMANENT) + res = SWTPM_NVRAM_Store_Permanent(); + if (res == 0 && blobtype == PTM_BLOB_TYPE_VOLATILE) res = SWTPM_NVRAM_Store_Volatile(); diff --git a/src/swtpm/swtpm_nvstore.c b/src/swtpm/swtpm_nvstore.c index 361babde..52ddf50d 100644 --- a/src/swtpm/swtpm_nvstore.c +++ b/src/swtpm/swtpm_nvstore.c @@ -481,6 +481,37 @@ TPM_RESULT SWTPM_NVRAM_Store_Volatile(void) return rc; } +/* + * Flush libtpms's in-memory PERMANENT state to disk so that the next + * SWTPM_NVRAM_GetStateBlob(PERMANENT) returns a blob consistent with what + * libtpms is actually running. Counterpart to SWTPM_NVRAM_Store_Volatile(). + * + * Without this, GET_STATEBLOB(PERMANENT) on the migration-out path reads a + * file that may be missing, empty, or stale relative to libtpms RAM (the + * file is only written by _plat__NvCommit() on UT_NV-class commands), and + * an empty/short-read blob then poisons the destination's state restore. + */ +TPM_RESULT SWTPM_NVRAM_Store_Permanent(void) +{ + TPM_RESULT rc = 0; + char *name = TPM_PERMANENT_ALL_NAME; + uint32_t tpm_number = 0; + unsigned char *buffer = NULL; + uint32_t buflen = 0; + + TPM_DEBUG(" SWTPM_Store_Permanent: Name %s\n", name); + if (rc == 0) { + rc = TPMLIB_GetState(TPMLIB_STATE_PERMANENT, &buffer, &buflen); + } + if (rc == 0 && buflen > 0) { + rc = SWTPM_NVRAM_StoreData(buffer, buflen, tpm_number, name); + } + + free(buffer); + + return rc; +} + static TPM_RESULT SWTPM_NVRAM_KeyParamCheck(uint32_t keylen, enum encryption_mode encmode) diff --git a/src/swtpm/swtpm_nvstore.h b/src/swtpm/swtpm_nvstore.h index cab3fa94..eeb891ae 100644 --- a/src/swtpm/swtpm_nvstore.h +++ b/src/swtpm/swtpm_nvstore.h @@ -69,6 +69,7 @@ TPM_RESULT SWTPM_NVRAM_DeleteName(uint32_t tpm_number, const char *name, TPM_BOOL mustExist); TPM_RESULT SWTPM_NVRAM_Store_Volatile(void); +TPM_RESULT SWTPM_NVRAM_Store_Permanent(void); TPM_RESULT SWTPM_NVRAM_Set_FileKey(const unsigned char *data, uint32_t length,