From dedb31e76df69ac8be5d7ff62d7dd47d98decb5b Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Wed, 4 Nov 2026 01:34:12 +0000 Subject: [PATCH] address review: make ScriptSandbox !Send so it drops on the thread that entered it --- crates/engine/src/io.rs | 4 ++-- crates/engine/src/io/tests.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/io.rs b/crates/engine/src/io.rs index 8c56cccd6..e25c2373c 100644 --- a/crates/engine/src/io.rs +++ b/crates/engine/src/io.rs @@ -755,12 +755,12 @@ thread_local! { /// While one is alive, [`read_file`] and [`write_file`] on this thread refuse every path: a /// stored script's steps run inside one (#2178), so no command reaches the file system from a /// script whatever its parameters are called. Dropping it (also on unwind) lifts the refusal. -pub struct ScriptSandbox(()); +pub struct ScriptSandbox(std::marker::PhantomData<*const ()>); impl ScriptSandbox { pub fn enter() -> Self { let _ = SCRIPT_SANDBOX.try_with(|c| c.set(c.get().saturating_add(1))); - ScriptSandbox(()) + ScriptSandbox(std::marker::PhantomData) } } diff --git a/crates/engine/src/io/tests.rs b/crates/engine/src/io/tests.rs index 9051c5c09..4b7fe2023 100644 --- a/crates/engine/src/io/tests.rs +++ b/crates/engine/src/io/tests.rs @@ -351,6 +351,19 @@ fn script_sandbox_refuses_file_access_until_dropped() { let _ = std::fs::remove_dir_all(&dir); } +/// The guard's drop lowers the counter of the thread it runs on, so it must not move between threads: +/// naming the trait's method is ambiguous (a compile error) if `ScriptSandbox` is `Send`. +#[test] +fn script_sandbox_is_not_send() { + trait AmbiguousIfSend { + fn check() {} + } + impl AmbiguousIfSend<()> for T {} + struct IsSend; + impl AmbiguousIfSend for T {} + >::check(); +} + /// A minimal compound (CFB/OLE2) file — FAT in sector 0, directory in sector 1 — holding a /// root entry and the given stream names. fn cfb(streams: &[&str]) -> Vec {