From 5cd3462014a1383514142b7d0863642148d878ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 8 Mar 2026 20:51:38 +0100 Subject: [PATCH 1/4] vmspawn: make efi variable nvram dependent on whether the EFI profile knows the concept, not on secureboot --- src/vmspawn/vmspawn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index c114693d91..8f08fdb81c 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -2367,7 +2367,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { return log_oom(); _cleanup_(unlink_and_freep) char *ovmf_vars_to = NULL; - if (ovmf_config->supports_sb) { + if (ovmf_config->vars) { const char *ovmf_vars_from = ovmf_config->vars; _cleanup_free_ char *escaped_ovmf_vars_to = NULL; _cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF; From 9996cfd2c9e43d981edeeecfd16aed8d4b0876a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 8 Mar 2026 21:52:08 +0100 Subject: [PATCH 2/4] vmspawn: manage EFI nvram (variables) state similar to TPM state --- man/systemd-vmspawn.xml | 17 ++++ src/vmspawn/vmspawn.c | 173 ++++++++++++++++++++++++++++------------ 2 files changed, 141 insertions(+), 49 deletions(-) diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 136bd65340..331c7c16fd 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -226,6 +226,23 @@ + + + + Configures where to place the EFI variable NVRAM state. This takes an absolute file + system path to a regular file to persistently place the state in. If the file is missing it is + created as needed. If set to the special string auto a persistent path is + automatically derived from the VM image path or directory path, with the + .efinvramstate suffix appended. If set to the special string + off the EFI variable NVRAM state is only maintained transiently and flushed out + when the VM shuts down. Defaults to auto. + + If is specified, auto behaves like + off. + + + + diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 8f08fdb81c..fa433ba504 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -88,13 +88,15 @@ #define VM_TAP_HASH_KEY SD_ID128_MAKE(01,d0,c6,4c,2b,df,24,fb,c0,f8,b2,09,7d,59,b2,93) -typedef enum TpmStateMode { - TPM_STATE_OFF, /* keep no state around */ - TPM_STATE_AUTO, /* keep state around if not ephemeral, derive path from image/directory */ - TPM_STATE_PATH, /* explicitly specified location */ - _TPM_STATE_MODE_MAX, - _TPM_STATE_MODE_INVALID = -EINVAL, -} TpmStateMode; +/* An enum controlling how auxiliary state for the VM are maintained, i.e. the TPM state and the EFI variable + * NVRAM. */ +typedef enum StateMode { + STATE_OFF, /* keep no state around */ + STATE_AUTO, /* keep state around if not ephemeral, derive path from image/directory */ + STATE_PATH, /* explicitly specified location */ + _STATE_MODE_MAX, + _STATE_MODE_INVALID = -EINVAL, +} StateMode; typedef struct SSHInfo { unsigned cid; @@ -144,7 +146,9 @@ static struct ether_addr arg_network_provided_mac = {}; static char **arg_smbios11 = NULL; static uint64_t arg_grow_image = 0; static char *arg_tpm_state_path = NULL; -static TpmStateMode arg_tpm_state_mode = TPM_STATE_AUTO; +static StateMode arg_tpm_state_mode = STATE_AUTO; +static char *arg_efi_nvram_state_path = NULL; +static StateMode arg_efi_nvram_state_mode = STATE_AUTO; static bool arg_ask_password = true; static bool arg_notify_ready = true; static char **arg_bind_user = NULL; @@ -171,6 +175,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_background, freep); STATIC_DESTRUCTOR_REGISTER(arg_ssh_key_type, freep); STATIC_DESTRUCTOR_REGISTER(arg_smbios11, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm_state_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_efi_nvram_state_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep); @@ -209,6 +214,8 @@ static int help(void) { " --tpm=BOOL Enable use of a virtual TPM\n" " --tpm-state=off|auto|PATH\n" " Where to store TPM state\n" + " --efi-nvram-state=off|auto|PATH\n" + " Where to store EFI Variable NVRAM state\n" " --linux=PATH Specify the linux kernel for direct kernel boot\n" " --initrd=PATH Specify the initrd for direct kernel boot\n" " -n --network-tap Create a TAP device for networking\n" @@ -317,6 +324,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_CONSOLE, ARG_BACKGROUND, ARG_TPM_STATE, + ARG_EFI_NVRAM_STATE, ARG_NO_ASK_PASSWORD, ARG_PROPERTY, ARG_NOTIFY_READY, @@ -374,6 +382,7 @@ static int parse_argv(int argc, char *argv[]) { { "smbios11", required_argument, NULL, 's' }, { "grow-image", required_argument, NULL, 'G' }, { "tpm-state", required_argument, NULL, ARG_TPM_STATE }, + { "efi-nvram-state", required_argument, NULL, ARG_EFI_NVRAM_STATE }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { "property", required_argument, NULL, ARG_PROPERTY }, { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, @@ -710,7 +719,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - arg_tpm_state_mode = TPM_STATE_PATH; + arg_tpm_state_mode = STATE_PATH; break; } @@ -720,10 +729,30 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse --tpm-state= parameter: %s", optarg); - arg_tpm_state_mode = r ? TPM_STATE_AUTO : TPM_STATE_OFF; + arg_tpm_state_mode = r ? STATE_AUTO : STATE_OFF; arg_tpm_state_path = mfree(arg_tpm_state_path); break; + case ARG_EFI_NVRAM_STATE: + if (path_is_valid(optarg) && (path_is_absolute(optarg) || path_startswith(optarg, "./"))) { + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_efi_nvram_state_path); + if (r < 0) + return r; + + arg_efi_nvram_state_mode = STATE_PATH; + break; + } + + r = isempty(optarg) ? false : + streq(optarg, "auto") ? true : + parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --efi-nvram-state= parameter: %s", optarg); + + arg_efi_nvram_state_mode = r ? STATE_AUTO : STATE_OFF; + arg_efi_nvram_state_path = mfree(arg_efi_nvram_state_path); + break; + case ARG_NO_ASK_PASSWORD: arg_ask_password = false; break; @@ -1948,6 +1977,30 @@ static int on_request_stop(sd_bus_message *m, void *userdata, sd_bus_error *erro return 0; } +static int make_sidecar_path(const char *suffix, char **ret) { + int r; + + assert(suffix); + assert(ret); + + const char *p = ASSERT_PTR(arg_image ?: arg_directory); + + _cleanup_free_ char *parent = NULL, *filename = NULL; + r = path_split_prefix_filename(p, &parent, &filename); + if (r < 0) + return log_error_errno(r, "Failed to extract parent directory and filename from '%s': %m", p); + + if (!strextend(&filename, suffix)) + return log_oom(); + + _cleanup_free_ char *j = path_join(parent, filename); + if (!j) + return log_oom(); + + *ret = TAKE_PTR(j); + return 0; +} + static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL; _cleanup_free_ char *qemu_binary = NULL, *mem = NULL, *kernel = NULL; @@ -2366,30 +2419,67 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return log_oom(); - _cleanup_(unlink_and_freep) char *ovmf_vars_to = NULL; - if (ovmf_config->vars) { - const char *ovmf_vars_from = ovmf_config->vars; - _cleanup_free_ char *escaped_ovmf_vars_to = NULL; - _cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF; + if (arg_efi_nvram_state_mode == STATE_AUTO && !arg_ephemeral) { + assert(!arg_efi_nvram_state_path); - r = tempfn_random_child(NULL, "vmspawn-", &ovmf_vars_to); + r = make_sidecar_path(".efinvramstate", &arg_efi_nvram_state_path); if (r < 0) return r; - source_fd = open(ovmf_vars_from, O_RDONLY|O_CLOEXEC); - if (source_fd < 0) - return log_error_errno(source_fd, "Failed to open OVMF vars file %s: %m", ovmf_vars_from); + log_debug("Storing EFI NVRAM state persistently under '%s'.", arg_efi_nvram_state_path); + } - target_fd = open(ovmf_vars_to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); - if (target_fd < 0) - return log_error_errno(errno, "Failed to create regular file for OVMF vars at %s: %m", ovmf_vars_to); + _cleanup_(unlink_and_freep) char *ovmf_vars = NULL; + if (ovmf_config->vars) { + _cleanup_close_ int target_fd = -EBADF; + _cleanup_(unlink_and_freep) char *destroy_path = NULL; + bool newly_created; + const char *state; + if (arg_efi_nvram_state_path) { + _cleanup_free_ char *d = strdup(arg_efi_nvram_state_path); + if (!d) + return log_oom(); - r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK); - if (r < 0) - return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", ovmf_vars_from, ovmf_vars_to); + target_fd = openat_report_new(AT_FDCWD, arg_efi_nvram_state_path, O_WRONLY|O_CREAT|O_CLOEXEC, 0600, &newly_created); + if (target_fd < 0) + return log_error_errno(target_fd, "Failed to open file for OVMF vars at %s: %m", arg_efi_nvram_state_path); - /* This isn't always available so don't raise an error if it fails */ - (void) copy_times(source_fd, target_fd, 0); + if (newly_created) + destroy_path = TAKE_PTR(d); + + r = fd_verify_regular(target_fd); + if (r < 0) + return log_error_errno(r, "Not a regular file for OVMF variables at %s: %m", arg_efi_nvram_state_path); + + state = arg_efi_nvram_state_path; + } else { + _cleanup_free_ char *t = NULL; + r = tempfn_random_child(/* p= */ NULL, "vmspawn-", &t); + if (r < 0) + return log_error_errno(r, "Failed to create temporary filename: %m"); + + target_fd = open(t, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0600); + if (target_fd < 0) + return log_error_errno(errno, "Failed to create regular file for OVMF vars at %s: %m", t); + + newly_created = true; + state = ovmf_vars = TAKE_PTR(t); + } + + if (newly_created) { + _cleanup_close_ int source_fd = open(ovmf_config->vars, O_RDONLY|O_CLOEXEC); + if (source_fd < 0) + return log_error_errno(errno, "Failed to open OVMF vars file %s: %m", ovmf_config->vars); + + r = copy_bytes(source_fd, target_fd, UINT64_MAX, COPY_REFLINK); + if (r < 0) + return log_error_errno(r, "Failed to copy bytes from %s to %s: %m", ovmf_config->vars, state); + + /* This isn't always available so don't raise an error if it fails */ + (void) copy_times(source_fd, target_fd, 0); + } + + destroy_path = mfree(destroy_path); /* disarm auto-destroy */ r = strv_extend_many( &cmdline, @@ -2399,11 +2489,11 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (r < 0) return log_oom(); - escaped_ovmf_vars_to = escape_qemu_value(ovmf_vars_to); - if (!escaped_ovmf_vars_to) + _cleanup_free_ char *escaped_state = escape_qemu_value(state); + if (!escaped_state) return log_oom(); - r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_ovmf_vars_to, ovmf_config_format(ovmf_config)); + r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_state, ovmf_config_format(ovmf_config)); if (r < 0) return log_oom(); } @@ -2669,33 +2759,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM not supported on %s, refusing", architecture_to_string(native_architecture())); if (arg_tpm < 0) { arg_tpm = false; - log_debug("TPM not support on %s, disabling tpm autodetection and continuing", architecture_to_string(native_architecture())); + log_debug("TPM not supported on %s, disabling tpm autodetection and continuing", architecture_to_string(native_architecture())); } } _cleanup_free_ char *swtpm = NULL; if (arg_tpm != 0) { - if (arg_tpm_state_mode == TPM_STATE_AUTO && !arg_ephemeral) { + if (arg_tpm_state_mode == STATE_AUTO && !arg_ephemeral) { assert(!arg_tpm_state_path); - const char *p = ASSERT_PTR(arg_image ?: arg_directory); - - _cleanup_free_ char *parent = NULL; - r = path_extract_directory(p, &parent); + r = make_sidecar_path(".tpmstate", &arg_tpm_state_path); if (r < 0) - return log_error_errno(r, "Failed to extract parent directory from '%s': %m", p); - - _cleanup_free_ char *filename = NULL; - r = path_extract_filename(p, &filename); - if (r < 0) - return log_error_errno(r, "Failed to extract filename from '%s': %m", p); - - if (!strextend(&filename, ".tpmstate")) - return log_oom(); - - arg_tpm_state_path = path_join(parent, filename); - if (!arg_tpm_state_path) - return log_oom(); + return r; log_debug("Storing TPM state persistently under '%s'.", arg_tpm_state_path); } From dc91cb82da2a599564d7ea7903c42b131d328f89 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 19 Mar 2026 14:20:02 +0100 Subject: [PATCH 3/4] vmspawn: split out swtpm-setup logic, and beef it up a bit --- src/shared/meson.build | 1 + src/shared/swtpm-util.c | 158 ++++++++++++++++++++++++++++++++++++++++ src/shared/swtpm-util.h | 4 + src/vmspawn/vmspawn.c | 44 +---------- 4 files changed, 167 insertions(+), 40 deletions(-) create mode 100644 src/shared/swtpm-util.c create mode 100644 src/shared/swtpm-util.h diff --git a/src/shared/meson.build b/src/shared/meson.build index 3d4808dafe..3088b419a5 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -187,6 +187,7 @@ shared_sources = files( 'socket-netlink.c', 'specifier.c', 'switch-root.c', + 'swtpm-util.c', 'tar-util.c', 'tmpfile-util-label.c', 'tomoyo-util.c', diff --git a/src/shared/swtpm-util.c b/src/shared/swtpm-util.c new file mode 100644 index 0000000000..836f877e12 --- /dev/null +++ b/src/shared/swtpm-util.c @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-json.h" + +#include "alloc-util.h" +#include "escape.h" +#include "fd-util.h" +#include "json-util.h" +#include "log.h" +#include "memfd-util.h" +#include "path-util.h" +#include "pidref.h" +#include "process-util.h" +#include "string-util.h" +#include "strv.h" +#include "swtpm-util.h" + +int manufacture_swtpm(const char *state_dir, const char *secret) { + int r; + + assert(state_dir); + + _cleanup_free_ char *swtpm_setup = NULL; + r = find_executable("swtpm_setup", &swtpm_setup); + if (r < 0) + return log_error_errno(r, "Failed to find 'swtpm_setup' binary: %m"); + + _cleanup_strv_free_ char **args = strv_new( + swtpm_setup, + "--tpm2", + "--print-profiles"); + if (!args) + return log_oom(); + + _cleanup_close_ int mfd = memfd_new("swtpm-profiles"); + if (mfd < 0) + return log_error_errno(mfd, "Failed to allocate memfd: %m"); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *cmdline = quote_command_line(args, SHELL_ESCAPE_EMPTY); + log_debug("About to spawn: %s", strnull(cmdline)); + } + + _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; + r = pidref_safe_fork_full( + "(swtpm-lprof)", + (int[]) { -EBADF, mfd, STDERR_FILENO }, + /* except_fds= */ NULL, + /* n_except_fds= */ 0, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_REARRANGE_STDIO|FORK_LOG, + &pidref); + if (r < 0) + return log_error_errno(r, "Failed to run swtpm_setup: %m"); + if (r == 0) { + /* Child */ + execvp(args[0], args); + log_error_errno(errno, "Failed to execute '%s': %m", args[0]); + _exit(EXIT_FAILURE); + } + + r = pidref_wait_for_terminate_and_check("(swtpm-lprof)", &pidref, WAIT_LOG_ABNORMAL); + if (r < 0) + return r; + + /* NB: we ignore the exit status of --print-profiles, it's broken. Instead we check if we have + * received a valid JSON object via STDOUT. */ + (void) r; + + _cleanup_free_ char *text = NULL; + r = read_full_file_full( + mfd, + /* filename= */ NULL, + /* offset= */ 0, + /* size= */ SIZE_MAX, + /* flags= */ 0, + /* bind_name= */ NULL, + &text, + /* ret_size= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to read memory fd: %m"); + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL; + const char *best_profile = NULL; + if (isempty(text)) + log_notice("No list of supported profiles could be acquired from swtpm, assuming the implementation is too old to know the concept of profiles."); + else { + r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &j, /* reterr_line= */ NULL, /* reterr_column= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse swtpm's --print-profiles output: %m"); + + sd_json_variant *v = sd_json_variant_by_key(j, "builtin"); + if (v) { + if (!sd_json_variant_is_array(v)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'builtin' field is not an array: %m"); + + sd_json_variant *i; + JSON_VARIANT_ARRAY_FOREACH(i, v) { + if (!sd_json_variant_is_object(i)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile object is not a JSON object."); + + sd_json_variant *n = sd_json_variant_by_key(i, "Name"); + if (!n) { + log_debug("Object in profiles array does not have a 'Name', skipping."); + continue; + } + + if (!sd_json_variant_is_string(n)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile's 'Name' field is not a string."); + + const char *s = sd_json_variant_string(n); + + /* Pick the best of the default-v1, default-v2, … profiles */ + if (!startswith(s, "default-v")) + continue; + if (!best_profile || strverscmp_improved(s, best_profile) > 0) + best_profile = s; + } + } + } + + strv_free(args); + args = strv_new(swtpm_setup, + "--tpm-state", state_dir, + "--tpm2", + "--pcr-banks", "sha256", + "--ecc", + "--createek", + "--create-ek-cert", + "--create-platform-cert", + "--not-overwrite"); + if (!args) + return log_oom(); + + if (secret && strv_extendf(&args, "--keyfile=%s", secret) < 0) + return log_oom(); + + if (best_profile && strv_extendf(&args, "--profile-name=%s", best_profile) < 0) + return log_oom(); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *cmdline = quote_command_line(args, SHELL_ESCAPE_EMPTY); + log_debug("About to spawn: %s", strnull(cmdline)); + } + + r = pidref_safe_fork("(swtpm-setup)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, /* ret= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to run swtpm_setup: %m"); + if (r == 0) { + /* Child */ + execvp(args[0], args); + log_error_errno(errno, "Failed to execute '%s': %m", args[0]); + _exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/src/shared/swtpm-util.h b/src/shared/swtpm-util.h new file mode 100644 index 0000000000..9c1c737721 --- /dev/null +++ b/src/shared/swtpm-util.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int manufacture_swtpm(const char *state_dir, const char *secret); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index fa433ba504..a65f879449 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -72,6 +72,7 @@ #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "swtpm-util.h" #include "sync-util.h" #include "terminal-util.h" #include "tmpfile-util.h" @@ -1354,48 +1355,11 @@ static int start_tpm( if (r < 0) return log_error_errno(r, "Failed to create TPM state directory '%s': %m", state_dir); - _cleanup_free_ char *swtpm_setup = NULL; - r = find_executable("swtpm_setup", &swtpm_setup); + r = manufacture_swtpm(state_dir, /* secret= */ NULL); if (r < 0) - return log_error_errno(r, "Failed to find swtpm_setup binary: %m"); + return r; - /* Try passing --profile-name default-v2 first, in order to support RSA4096 pcrsig keys, which was - * added in 0.11. */ - _cleanup_strv_free_ char **argv = strv_new( - swtpm_setup, - "--tpm-state", state_dir, - "--tpm2", - "--pcr-banks", "sha256", - "--not-overwrite", - "--profile-name", "default-v2"); - if (!argv) - return log_oom(); - - r = pidref_safe_fork("(swtpm-setup)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, /* ret= */ NULL); - if (r == 0) { - /* Child */ - execvp(argv[0], argv); - log_error_errno(errno, "Failed to execute '%s': %m", argv[0]); - _exit(EXIT_FAILURE); - } - if (r == -EPROTO) { - /* If swtpm_setup fails, try again removing the default-v2 profile, as it might be an older - * version. */ - strv_remove(argv, "--profile-name"); - strv_remove(argv, "default-v2"); - - r = pidref_safe_fork("(swtpm-setup)", FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, /* ret= */ NULL); - if (r == 0) { - /* Child */ - execvp(argv[0], argv); - log_error_errno(errno, "Failed to execute '%s': %m", argv[0]); - _exit(EXIT_FAILURE); - } - } - if (r < 0) - return log_error_errno(r, "Failed to run swtpm_setup: %m"); - - strv_free(argv); + _cleanup_strv_free_ char **argv = NULL; argv = strv_new(sd_socket_activate, "--listen", listen_address, swtpm, "socket", "--tpm2", "--tpmstate"); if (!argv) return log_oom(); From 48ca2f6ff5c04c3099911938010679ba370d46b4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 9 Mar 2026 08:46:36 +0100 Subject: [PATCH 4/4] discover-image: remove tpm state + efi nvram state on image removal --- src/shared/discover-image.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index dd92452315..6b4493b960 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -142,7 +142,9 @@ static const char auxiliary_suffixes_nulstr[] = ".roothash.p7s\0" ".usrhash\0" ".usrhash.p7s\0" - ".verity\0"; + ".verity\0" + ".raw.tpmstate\0" + ".raw.efinvramstate\0"; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(image_dirname, ImageClass);