tpm2-setup: make NV index space exhaustion issues more discoverable

Let's log about this explicitly, and include a message catalog entry for
it.
This commit is contained in:
Lennart Poettering
2026-02-26 11:23:08 +01:00
parent a01912ef29
commit ac8266c7c2
4 changed files with 46 additions and 16 deletions

View 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).

View 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);

View 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

View 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[]) {