diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md
index a3a79bbf86c..ac964d771e6 100644
--- a/docs/BOOT_LOADER_INTERFACE.md
+++ b/docs/BOOT_LOADER_INTERFACE.md
@@ -191,11 +191,11 @@ Variables will be listed below using the Linux efivarfs naming,
NUL-terminated UTF-16 string, e.g. `1`) into which the boot loader measured
select SMBIOS structures: type 1 (system information, with the volatile
"Wake-up Type" field zeroed out), type 2 (baseboard information) and type 11
- (OEM strings). This is a volatile (non-persistent) variable, set only if a
- measurement was successfully completed, and remains unset otherwise. Both
- `systemd-boot` and `systemd-stub` perform this measurement; whichever runs
- first sets the variable, and its presence suppresses a second measurement of
- the same data into the same PCR during the same boot.
+ (OEM strings). This is a volatile (non-persistent) variable, set only if all
+ of these structures were measured successfully, and remains unset otherwise.
+ Both `systemd-boot` and `systemd-stub` perform this measurement; whichever
+ runs first sets the variable, and its presence suppresses a second
+ measurement of the same data into the same PCR during the same boot.
* The EFI variable `LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f`
contains the version string of the boot loader that was booted,
diff --git a/docs/TPM2_PCR_MEASUREMENTS.md b/docs/TPM2_PCR_MEASUREMENTS.md
index ce5dca66efa..9ffe7d867c0 100644
--- a/docs/TPM2_PCR_MEASUREMENTS.md
+++ b/docs/TPM2_PCR_MEASUREMENTS.md
@@ -156,11 +156,12 @@ described above.
This measurement is also performed by `systemd-stub` (see below), so that systems
that boot a UKI directly, bypassing `systemd-boot`, still get it. Whichever
-component runs first performs the measurement and sets the volatile
-`LoaderPcrSMBIOS` EFI variable to the PCR index used; its presence suppresses a
-second measurement of the same data into the same PCR during the same boot. Note
-that the firmware itself typically also extends PCR 1, so its final value is not
-solely determined by this measurement.
+component runs first performs the measurement and, if all structures were
+measured successfully, sets the volatile `LoaderPcrSMBIOS` EFI variable to the
+PCR index used; its presence suppresses a second measurement of the same data
+into the same PCR during the same boot. Note that the firmware itself typically
+also extends PCR 1, so its final value is not solely determined by this
+measurement.
### PCR 5, `EV_EVENT_TAG`, `loader.conf`
diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index 6f8ae255605..d81910b0cb8 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -631,9 +631,9 @@ extra /6a9857a393724b7a981ebb5b8495b9ea/1.2.3/baz.confext.rawThe PCR register index select SMBIOS structures are measured into — type 1
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
- 1). Set by the boot loader if a measurement was successfully completed, and remains
- unset otherwise. (Note that systemd-stub performs the same measurement when booted
- directly, bypassing the boot loader.)
+ 1). Set by the boot loader only if all of these structures were measured
+ successfully, and remains unset otherwise. (Note that systemd-stub performs the
+ same measurement when booted directly, bypassing the boot loader.)
diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml
index 966a700fe92..66171a5f982 100644
--- a/man/systemd-stub.xml
+++ b/man/systemd-stub.xml
@@ -671,10 +671,10 @@
The PCR register index select SMBIOS structures are measured into — type 1
(system information, with the volatile "Wake-up Type" field zeroed out), type 2 (baseboard
information) and type 11 (OEM strings). Formatted as decimal ASCII string (e.g.
- 1). This variable is set if a measurement was successfully completed, and remains
- unset otherwise. systemd-stub performs this measurement only if it has not already
- been done in the same boot (e.g. by the boot loader), as indicated by this variable being set
- already.
+ 1). This variable is set only if all of these structures were measured
+ successfully, and remains unset otherwise. systemd-stub performs this measurement
+ only if it has not already been done in the same boot (e.g. by the boot loader), as indicated by this
+ variable being set already.
diff --git a/src/boot/measure-smbios.c b/src/boot/measure-smbios.c
index 89da7d9e8f9..0b46e4951bb 100644
--- a/src/boot/measure-smbios.c
+++ b/src/boot/measure-smbios.c
@@ -8,19 +8,24 @@
#include "tpm2-pcr.h"
#include "util.h"
+typedef struct MeasureSmbiosContext {
+ bool measured; /* At least one structure was measured */
+ bool failed; /* At least one measurement failed */
+} MeasureSmbiosContext;
+
static void measure_smbios_raw(
const void *p,
size_t size,
uint32_t event_id,
const char16_t *description,
- bool *measured) {
+ MeasureSmbiosContext *ctx) {
EFI_STATUS err;
bool m = false;
assert(p);
assert(description);
- assert(measured);
+ assert(ctx);
err = tpm_log_tagged_event(
TPM2_PCR_PLATFORM_CONFIG,
@@ -29,15 +34,18 @@ static void measure_smbios_raw(
event_id,
description,
&m);
- if (err != EFI_SUCCESS)
- log_error_status(err, "Unable to measure SMBIOS structure (%ls), ignoring: %m", description);
+ if (err != EFI_SUCCESS) {
+ log_error_status(err, "Unable to measure SMBIOS structure (%ls): %m", description);
+ ctx->failed = true;
+ return;
+ }
- *measured = *measured || m;
+ ctx->measured = ctx->measured || m;
}
-static void measure_smbios_type1(const SmbiosHeader *header, size_t size, bool *measured) {
+static void measure_smbios_type1(const SmbiosHeader *header, size_t size, MeasureSmbiosContext *ctx) {
assert(header);
- assert(measured);
+ assert(ctx);
/* The wake-up type field varies depending on how the machine was powered on (cold boot, resume
* from sleep, AC restore, …), which would make the measurement non-reproducible. Hence measure a
@@ -61,24 +69,24 @@ static void measure_smbios_type1(const SmbiosHeader *header, size_t size, bool *
p = copy;
}
- measure_smbios_raw(p, size, SMBIOS_TYPE1_EVENT_TAG_ID, u"smbios:type1", measured);
+ measure_smbios_raw(p, size, SMBIOS_TYPE1_EVENT_TAG_ID, u"smbios:type1", ctx);
}
static bool measure_smbios_object(const SmbiosHeader *header, size_t size, void *userdata) {
- bool *measured = ASSERT_PTR(userdata);
+ MeasureSmbiosContext *ctx = ASSERT_PTR(userdata);
switch (header->type) {
case 1: /* System Information */
- measure_smbios_type1(header, size, measured);
+ measure_smbios_type1(header, size, ctx);
break;
case 2: /* Baseboard Information */
- measure_smbios_raw(header, size, SMBIOS_TYPE2_EVENT_TAG_ID, u"smbios:type2", measured);
+ measure_smbios_raw(header, size, SMBIOS_TYPE2_EVENT_TAG_ID, u"smbios:type2", ctx);
break;
case 11: /* OEM Strings */
- measure_smbios_raw(header, size, SMBIOS_TYPE11_EVENT_TAG_ID, u"smbios:type11", measured);
+ measure_smbios_raw(header, size, SMBIOS_TYPE11_EVENT_TAG_ID, u"smbios:type11", ctx);
break;
default:
@@ -89,7 +97,7 @@ static bool measure_smbios_object(const SmbiosHeader *header, size_t size, void
}
void measure_smbios(void) {
- bool measured = false;
+ MeasureSmbiosContext ctx = {};
if (!runtime_measurement_available())
return;
@@ -101,9 +109,9 @@ void measure_smbios(void) {
/* Measure SMBIOS type 1 (system information), type 2 (baseboard information) and type 11 (OEM
* strings) into PCR 1, in a single pass over the SMBIOS table. */
- smbios_foreach(measure_smbios_object, &measured);
+ smbios_foreach(measure_smbios_object, &ctx);
- /* If we measured something, tell the OS which PCR we used (and suppress a second pass). */
- if (measured)
+ /* Tell the OS which PCR we used (and suppress a second pass), but only if every structure was measured. */
+ if (ctx.measured && !ctx.failed)
(void) efivar_set_uint64_str16(MAKE_GUID_PTR(LOADER), u"LoaderPcrSMBIOS", TPM2_PCR_PLATFORM_CONFIG, /* flags= */ 0);
}
diff --git a/src/boot/measure-smbios.h b/src/boot/measure-smbios.h
index 758b2c45b43..35f98e7d584 100644
--- a/src/boot/measure-smbios.h
+++ b/src/boot/measure-smbios.h
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-/* Measures SMBIOS type 1 (system information, with the volatile "Wake-up Type" field masked) and all
- * type 11 (OEM strings) structures into PCR 1, and records the PCR index in the transient
- * LoaderPcrSMBIOS EFI variable. Called by both sd-boot and sd-stub; the presence of LoaderPcrSMBIOS
- * suppresses a redundant second measurement when both run during the same boot. */
+/* Measures SMBIOS type 1 (system information, with the volatile "Wake-up Type" field masked), type 2
+ * (baseboard information) and all type 11 (OEM strings) structures into PCR 1, and records the PCR
+ * index in the transient LoaderPcrSMBIOS EFI variable if all of them were measured successfully.
+ * Called by both sd-boot and sd-stub; the presence of LoaderPcrSMBIOS suppresses a redundant second
+ * measurement when both run during the same boot. */
void measure_smbios(void);
diff --git a/src/shared/efi-loader.c b/src/shared/efi-loader.c
index e05afb1da1d..ae8f9699e16 100644
--- a/src/shared/efi-loader.c
+++ b/src/shared/efi-loader.c
@@ -347,8 +347,9 @@ int efi_measured_smbios(int log_level) {
if (cached >= 0)
return cached;
- /* Whether sd-boot or sd-stub measured the SMBIOS table (types 1, 2, and 11) into PCR 1. Firmware
- * does not do this for OEM strings: edk2's SmbiosMeasurementDxe zeros type 11 before hashing. */
+ /* Whether sd-boot or sd-stub measured the SMBIOS table (types 1, 2, and 11) into PCR 1. The loader
+ * sets the variable only if all of these structures were measured. Firmware does not do this for
+ * OEM strings: edk2's SmbiosMeasurementDxe zeros type 11 before hashing. */
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderPcrSMBIOS"), &pcr_string);
if (r == -ENOENT)