From bba596f79a0590f58d04898ff5b40782fef43594 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 24 Jul 2026 17:37:29 +0000 Subject: [PATCH 1/5] fix(simulator): expose GCP TPM event log --- dstack/tee-simulator/src/tpm.rs | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/dstack/tee-simulator/src/tpm.rs b/dstack/tee-simulator/src/tpm.rs index 70b2ea00b..88cc929d3 100644 --- a/dstack/tee-simulator/src/tpm.rs +++ b/dstack/tee-simulator/src/tpm.rs @@ -6,6 +6,7 @@ //! template and certificate NV indices consumed by `tpm-attest`. use std::{ + ffi::CString, io::{Read, Write}, os::{ fd::{AsRawFd, FromRawFd}, @@ -117,6 +118,7 @@ pub fn start_gcp_vtpm(runtime_dir: &Path, config: &TeeSimulatorConfig) -> Result } command("tpm2_startup", &["-c"])?; replay_fixture_event_log()?; + install_fixture_event_log()?; let template_with_size = state_dir.join("ak.tpm2b-public"); let generated_public = state_dir.join("ak.public"); @@ -213,6 +215,45 @@ fn replay_fixture_event_log() -> Result<()> { Ok(()) } +fn install_fixture_event_log() -> Result<()> { + let security_root = Path::new("/sys/kernel/security"); + let event_log = security_root.join("tpm0/binary_bios_measurements"); + if event_log.exists() { + return Ok(()); + } + let tpm_dir = event_log.parent().context("TPM event log has no parent")?; + if let Err(error) = fs_err::create_dir_all(tpm_dir) { + if !matches!(error.raw_os_error(), Some(libc::EPERM) | Some(libc::EACCES)) { + return Err(error).context("failed to create simulated TPM event-log directory"); + } + let source = CString::new("dstack-tee-simulator")?; + let target = CString::new("/sys/kernel/security")?; + let fstype = CString::new("tmpfs")?; + let data = CString::new("mode=0755")?; + let rc = unsafe { + libc::mount( + source.as_ptr(), + target.as_ptr(), + fstype.as_ptr(), + libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, + data.as_ptr().cast(), + ) + }; + if rc != 0 { + return Err(std::io::Error::last_os_error()) + .context("failed to mount simulated securityfs shadow"); + } + fs_err::create_dir_all(tpm_dir) + .context("failed to create simulated TPM event-log directory")?; + } + fs_err::write( + event_log, + include_bytes!("../../cc-eventlog/samples/tpm_eventlog.bin"), + ) + .context("failed to install simulated TPM event log")?; + Ok(()) +} + fn create_tpm_device_node() -> Result<()> { if Path::new("/dev/tpm0").exists() { return Ok(()); From 32cd940f6c222ef0b286f7a7ad4c5ad7c6ee4a75 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 24 Jul 2026 17:48:33 +0000 Subject: [PATCH 2/5] fix(simulator): shadow securityfs for GCP event log --- dstack/tee-simulator/src/tpm.rs | 44 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/dstack/tee-simulator/src/tpm.rs b/dstack/tee-simulator/src/tpm.rs index 88cc929d3..2f5746a04 100644 --- a/dstack/tee-simulator/src/tpm.rs +++ b/dstack/tee-simulator/src/tpm.rs @@ -222,30 +222,28 @@ fn install_fixture_event_log() -> Result<()> { return Ok(()); } let tpm_dir = event_log.parent().context("TPM event log has no parent")?; - if let Err(error) = fs_err::create_dir_all(tpm_dir) { - if !matches!(error.raw_os_error(), Some(libc::EPERM) | Some(libc::EACCES)) { - return Err(error).context("failed to create simulated TPM event-log directory"); - } - let source = CString::new("dstack-tee-simulator")?; - let target = CString::new("/sys/kernel/security")?; - let fstype = CString::new("tmpfs")?; - let data = CString::new("mode=0755")?; - let rc = unsafe { - libc::mount( - source.as_ptr(), - target.as_ptr(), - fstype.as_ptr(), - libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, - data.as_ptr().cast(), - ) - }; - if rc != 0 { - return Err(std::io::Error::last_os_error()) - .context("failed to mount simulated securityfs shadow"); - } - fs_err::create_dir_all(tpm_dir) - .context("failed to create simulated TPM event-log directory")?; + // securityfs does not permit userspace to create a synthetic TPM event + // log hierarchy. Shadow it in this development-only guest before + // publishing the fixture that was replayed into the simulated PCRs. + let source = CString::new("dstack-tee-simulator")?; + let target = CString::new("/sys/kernel/security")?; + let fstype = CString::new("tmpfs")?; + let data = CString::new("mode=0755")?; + let rc = unsafe { + libc::mount( + source.as_ptr(), + target.as_ptr(), + fstype.as_ptr(), + libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, + data.as_ptr().cast(), + ) + }; + if rc != 0 { + return Err(std::io::Error::last_os_error()) + .context("failed to mount simulated securityfs shadow"); } + fs_err::create_dir_all(tpm_dir) + .context("failed to create TPM event-log directory in securityfs shadow")?; fs_err::write( event_log, include_bytes!("../../cc-eventlog/samples/tpm_eventlog.bin"), From edc8c712189d5387f7da767698d2e1b41e44d468 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 31 Jul 2026 11:01:49 +0000 Subject: [PATCH 3/5] refactor(simulator): use safe securityfs mount wrapper --- dstack/tee-simulator/Cargo.toml | 1 + dstack/tee-simulator/src/tpm.rs | 29 +++++++++++------------------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/dstack/tee-simulator/Cargo.toml b/dstack/tee-simulator/Cargo.toml index b235d5467..18ed41738 100644 --- a/dstack/tee-simulator/Cargo.toml +++ b/dstack/tee-simulator/Cargo.toml @@ -21,6 +21,7 @@ dstack-types.workspace = true dstack-mr.workspace = true fuser.workspace = true libc.workspace = true +nix = { workspace = true, features = ["mount"] } sd-notify.workspace = true sha2.workspace = true tracing.workspace = true diff --git a/dstack/tee-simulator/src/tpm.rs b/dstack/tee-simulator/src/tpm.rs index 2f5746a04..bc142ef9f 100644 --- a/dstack/tee-simulator/src/tpm.rs +++ b/dstack/tee-simulator/src/tpm.rs @@ -6,7 +6,6 @@ //! template and certificate NV indices consumed by `tpm-attest`. use std::{ - ffi::CString, io::{Read, Write}, os::{ fd::{AsRawFd, FromRawFd}, @@ -225,23 +224,17 @@ fn install_fixture_event_log() -> Result<()> { // securityfs does not permit userspace to create a synthetic TPM event // log hierarchy. Shadow it in this development-only guest before // publishing the fixture that was replayed into the simulated PCRs. - let source = CString::new("dstack-tee-simulator")?; - let target = CString::new("/sys/kernel/security")?; - let fstype = CString::new("tmpfs")?; - let data = CString::new("mode=0755")?; - let rc = unsafe { - libc::mount( - source.as_ptr(), - target.as_ptr(), - fstype.as_ptr(), - libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC, - data.as_ptr().cast(), - ) - }; - if rc != 0 { - return Err(std::io::Error::last_os_error()) - .context("failed to mount simulated securityfs shadow"); - } + let flags = nix::mount::MsFlags::MS_NOSUID + | nix::mount::MsFlags::MS_NODEV + | nix::mount::MsFlags::MS_NOEXEC; + nix::mount::mount( + Some("dstack-tee-simulator"), + security_root, + Some("tmpfs"), + flags, + Some("mode=0755"), + ) + .context("failed to mount simulated securityfs shadow")?; fs_err::create_dir_all(tpm_dir) .context("failed to create TPM event-log directory in securityfs shadow")?; fs_err::write( From ba398bde0ba02ed61f0f88e153e8cda5e2d174b4 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 31 Jul 2026 11:02:14 +0000 Subject: [PATCH 4/5] build(simulator): record safe mount dependency --- dstack/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/dstack/Cargo.lock b/dstack/Cargo.lock index a0167f769..207388a0c 100644 --- a/dstack/Cargo.lock +++ b/dstack/Cargo.lock @@ -2161,6 +2161,7 @@ dependencies = [ "libc", "libloading", "mock-attestation", + "nix 0.29.0", "nsm-qvl", "pem", "reqwest", From 16ff3c6b3491afb7d233881f775384e583c1a182 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Fri, 31 Jul 2026 11:03:02 +0000 Subject: [PATCH 5/5] refactor(simulator): use safe descriptor flag wrappers --- dstack/tee-simulator/Cargo.toml | 2 +- dstack/tee-simulator/src/tpm.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dstack/tee-simulator/Cargo.toml b/dstack/tee-simulator/Cargo.toml index 18ed41738..07411d488 100644 --- a/dstack/tee-simulator/Cargo.toml +++ b/dstack/tee-simulator/Cargo.toml @@ -21,7 +21,7 @@ dstack-types.workspace = true dstack-mr.workspace = true fuser.workspace = true libc.workspace = true -nix = { workspace = true, features = ["mount"] } +nix = { workspace = true, features = ["fs", "mount"] } sd-notify.workspace = true sha2.workspace = true tracing.workspace = true diff --git a/dstack/tee-simulator/src/tpm.rs b/dstack/tee-simulator/src/tpm.rs index bc142ef9f..45dc4559d 100644 --- a/dstack/tee-simulator/src/tpm.rs +++ b/dstack/tee-simulator/src/tpm.rs @@ -293,12 +293,15 @@ pub fn run_nitro_vtpm(runtime_dir: &Path, config: &TeeSimulatorConfig) -> Result let generator = NsmGenerator::from_seed(parse_seed(seed)?)?; let (mut simulator, swtpm_stream) = UnixStream::pair()?; let swtpm_fd = swtpm_stream.as_raw_fd(); - let flags = unsafe { libc::fcntl(swtpm_fd, libc::F_GETFD) }; - anyhow::ensure!(flags >= 0, "failed to get swtpm socket flags"); - anyhow::ensure!( - unsafe { libc::fcntl(swtpm_fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC) } >= 0, - "failed to make swtpm socket inheritable" + let flags = nix::fcntl::FdFlag::from_bits_truncate( + nix::fcntl::fcntl(swtpm_fd, nix::fcntl::FcntlArg::F_GETFD) + .context("failed to get swtpm socket flags")?, ); + nix::fcntl::fcntl( + swtpm_fd, + nix::fcntl::FcntlArg::F_SETFD(flags - nix::fcntl::FdFlag::FD_CLOEXEC), + ) + .context("failed to make swtpm socket inheritable")?; let state_dir = std::env::temp_dir().join(format!("dstack-nitro-swtpm-{}", std::process::id())); fs_err::create_dir_all(&state_dir)?;