diff --git a/crates/openshell-sandbox/src/process.rs b/crates/openshell-sandbox/src/process.rs index 76786a84d..d004bb7d4 100644 --- a/crates/openshell-sandbox/src/process.rs +++ b/crates/openshell-sandbox/src/process.rs @@ -320,7 +320,14 @@ impl ProcessHandle { } let child = cmd.spawn().into_diagnostic()?; - let pid = child.id().unwrap_or(0); + let pid = child.id().ok_or_else(|| { + miette::miette!( + "Entrypoint process exited immediately after spawn (exec failed). \ + This typically means Landlock denied execute access to the entrypoint binary. \ + Check that the sandbox policy grants execute permission on the entrypoint path. \ + [program={program}]" + ) + })?; register_managed_child(pid); debug!(pid, program, "Process spawned"); @@ -416,7 +423,13 @@ impl ProcessHandle { } let child = cmd.spawn().into_diagnostic()?; - let pid = child.id().unwrap_or(0); + let pid = child.id().ok_or_else(|| { + miette::miette!( + "Entrypoint process exited immediately after spawn (exec failed). \ + This typically means the sandbox denied execute access to the entrypoint binary. \ + [program={program}]" + ) + })?; #[cfg(target_os = "linux")] register_managed_child(pid); diff --git a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs index 6b121e0ca..e7f37ce4f 100644 --- a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs +++ b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs @@ -176,7 +176,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result = (|| { let access_all = AccessFs::from_all(abi); - let access_read = AccessFs::from_read(abi); + let access_read = AccessFs::from_read(abi) | AccessFs::Execute; let mut ruleset = Ruleset::default(); ruleset = ruleset @@ -189,7 +189,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result