]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-setup: make NV index space exhaustion issues more discoverable 40841/head
authorLennart Poettering <lennart@amutable.com>
Thu, 26 Feb 2026 10:23:08 +0000 (11:23 +0100)
committerLennart Poettering <lennart@amutable.com>
Thu, 26 Feb 2026 10:34:59 +0000 (11:34 +0100)
Let's log about this explicitly, and include a message catalog entry for
it.

catalog/systemd.catalog.in
src/shared/tpm2-util.c
src/systemd/sd-messages.h
src/tpm2-setup/tpm2-setup.c

index 473edf2f07bcce333705ae2073d629204faca342..6f6ca9c4ea8514146d81c56509182c3089e63ec1 100644 (file)
@@ -1002,3 +1002,16 @@ device nodes appear early in boot, while regular users may appear only later.
 For devices managed by systemd-udevd, it is instead recommended to use the
 "uaccess"/"xaccess" mechanisms to grant limited and temporary access to device
 nodes, see sd-login(8).
+
+-- ab984ea008964fb88d6e389fb513fb94
+Subject: TPM NV index space exhausted, unable to initialize NvPCR
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The Trusted Platform Module's (TPM) NV index space has been exhausted, and an
+additional NvPCR, i.e. additional Platform Configuration Register (PCR) stored in
+non-volatile indexes (NV Indexes), could not be initialized.
+
+This typically means the persistent NV index memory available on the TPM is
+taken up by other resources, or is extremely limited. A TPM reset might help
+recovering space (but will invalidate all TPM bound keys and resources).
index 32ad35df49d5a0b96f833fe55d06455b0ff39e74..4de6f46aed60c8ccad4743c7fe4d3e47b36f7420 100644 (file)
@@ -6031,6 +6031,9 @@ int tpm2_define_nvpcr_nv_index(
                         /* auth= */ NULL,
                         &public_info,
                         &new_handle->esys_handle);
+        if (rc == TPM2_RC_NV_SPACE)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOSPC),
+                                       "NV index space on TPM exhausted, cannot allocate NvPCR.");
         if (rc == TPM2_RC_NV_DEFINED) {
                 log_debug("NV index 0x%" PRIu32 " already registered.", nv_index);
 
index 5465db8b1311f828f85aa7622c3c8476fd55e443..7bc4199408f13f42ed912445b296bdce05f8940a 100644 (file)
@@ -306,6 +306,9 @@ _SD_BEGIN_DECLARATIONS;
 #define SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED            SD_ID128_MAKE(34,05,20,5d,36,8e,49,fe,b5,ab,39,25,fe,e1,38,74)
 #define SD_MESSAGE_SYSTEM_ACCOUNT_REQUIRED_STR        SD_ID128_MAKE_STR(34,05,20,5d,36,8e,49,fe,b5,ab,39,25,fe,e1,38,74)
 
+#define SD_MESSAGE_TPM_INVINDEX_EXHAUSTED             SD_ID128_MAKE(ab,98,4e,a0,08,96,4f,b8,8d,6e,38,9f,b5,13,fb,94)
+#define SD_MESSAGE_TPM_INVINDEX_EXHAUSTED_STR         SD_ID128_MAKE_STR(ab,98,4e,a0,08,96,4f,b8,8d,6e,38,9f,b5,13,fb,94)
+
 _SD_END_DECLARATIONS;
 
 #endif
index 1d057ea0e24da6c9b39615c46bfbdfe4bb9ce14b..a811ea436dbee1c7daf6df44032dee10141d2c98 100644 (file)
@@ -385,7 +385,7 @@ static int setup_srk(void) {
 typedef struct SetupNvPCRContext {
         Tpm2Context *tpm2_context;
         struct iovec anchor_secret;
-        size_t n_already, n_anchored;
+        size_t n_already, n_anchored, n_failed;
         Set *done;
 } SetupNvPCRContext;
 
@@ -404,16 +404,11 @@ static int setup_nvpcr_one(
 
         assert(c);
         assert(name);
+        assert(c->tpm2_context);
 
         if (set_contains(c->done, name))
                 return 0;
 
-        if (!c->tpm2_context) {
-                r = tpm2_context_new_or_warn(arg_tpm2_device, &c->tpm2_context);
-                if (r < 0)
-                        return r;
-        }
-
         r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name, &c->anchor_secret);
         if (r == -EUNATCH) {
                 assert(!iovec_is_set(&c->anchor_secret));
@@ -427,8 +422,16 @@ static int setup_nvpcr_one(
 
                 r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name, &c->anchor_secret);
         }
-        if (r < 0)
+        if (r == -ENOSPC) {
+                c->n_failed++;
+                return log_struct_errno(LOG_ERR, r,
+                                        LOG_MESSAGE("The TPM's NV index space is exhausted, unable to allocate NvPCR '%s': %m", name),
+                                        LOG_MESSAGE_ID(SD_MESSAGE_TPM_INVINDEX_EXHAUSTED_STR));
+        }
+        if (r < 0) {
+                c->n_failed++;
                 return log_error_errno(r, "Failed to extend NvPCR index with anchor secret: %m");
+        }
 
         if (r > 0)
                 c->n_anchored++;
@@ -455,10 +458,17 @@ static int setup_nvpcr(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to find .nvpcr files: %m");
 
+        int ret = 0;
         STRV_FOREACH(i, l) {
-                r = setup_nvpcr_one(&c, *i);
-                if (r < 0)
-                        return r;
+                if (!c.tpm2_context) {
+                        /* Inability to contact the TPM shall be fatal for us */
+                        r = tpm2_context_new_or_warn(arg_tpm2_device, &c.tpm2_context);
+                        if (r < 0)
+                                return r;
+                }
+
+                /* But if we fail to initialize some NvPCR, we go on */
+                RET_GATHER(ret, setup_nvpcr_one(&c, *i));
         }
 
         if (c.n_already > 0 && c.n_anchored == 0 && !arg_early) {
@@ -466,11 +476,12 @@ static int setup_nvpcr(void) {
                  * have happened in the initrd, and thus the anchor ID was not committed to /var/ or the ESP
                  * yet. Hence, let's explicitly do so now, to catch up. */
 
-                r = tpm2_nvpcr_acquire_anchor_secret(/* ret= */ NULL, /* sync_secondary= */ true);
-                if (r < 0)
-                        return r;
+                RET_GATHER(ret, tpm2_nvpcr_acquire_anchor_secret(/* ret= */ NULL, /* sync_secondary= */ true));
         }
 
+        if (c.n_failed > 0)
+                log_warning("%zu NvPCRs failed to initialize, proceeding anyway.", c.n_failed);
+
         if (c.n_anchored > 0) {
                 if (c.n_already == 0)
                         log_info("%zu NvPCRs initialized.", c.n_anchored);
@@ -478,10 +489,10 @@ static int setup_nvpcr(void) {
                         log_info("%zu NvPCRs initialized. (%zu NvPCRs were already initialized.)", c.n_anchored, c.n_already);
         } else if (c.n_already > 0)
                 log_info("%zu NvPCRs already initialized.", c.n_already);
-        else
+        else if (c.n_failed == 0)
                 log_debug("No NvPCRs defined, nothing initialized.");
 
-        return r;
+        return ret;
 }
 
 static int run(int argc, char *argv[]) {