Эх сурвалжийг харах

fix(sandbox): probe unshare capability instead of binary existence

On GitHub Actions runners, `unshare` binary exists at /usr/bin/unshare
but user namespaces (CLONE_NEWUSER) are restricted, causing `unshare
--user --map-root-user` to silently fail. This produced empty stdout
in the bash_stdout_roundtrip parity test (mock_parity_harness.rs:533).

Replace the simple `command_exists("unshare")` check with
`unshare_user_namespace_works()` that actually probes whether
`unshare --user --map-root-user true` succeeds. Result is cached
via OnceLock so the probe runs at most once per process.

Fixes: CI red on main@85c5b0e (Rust CI run 23933274144)
Jobdori 2 сар өмнө
parent
commit
89104eb0a2

+ 22 - 1
rust/crates/runtime/src/sandbox.rs

@@ -161,7 +161,7 @@ pub fn resolve_sandbox_status(config: &SandboxConfig, cwd: &Path) -> SandboxStat
 #[must_use]
 pub fn resolve_sandbox_status_for_request(request: &SandboxRequest, cwd: &Path) -> SandboxStatus {
     let container = detect_container_environment();
-    let namespace_supported = cfg!(target_os = "linux") && command_exists("unshare");
+    let namespace_supported = cfg!(target_os = "linux") && unshare_user_namespace_works();
     let network_supported = namespace_supported;
     let filesystem_active =
         request.enabled && request.filesystem_mode != FilesystemIsolationMode::Off;
@@ -282,6 +282,27 @@ fn command_exists(command: &str) -> bool {
         .is_some_and(|paths| env::split_paths(&paths).any(|path| path.join(command).exists()))
 }
 
+/// Check whether `unshare --user` actually works on this system.
+/// On some CI environments (e.g. GitHub Actions), the binary exists but
+/// user namespaces are restricted, causing silent failures.
+fn unshare_user_namespace_works() -> bool {
+    use std::sync::OnceLock;
+    static RESULT: OnceLock<bool> = OnceLock::new();
+    *RESULT.get_or_init(|| {
+        if !command_exists("unshare") {
+            return false;
+        }
+        std::process::Command::new("unshare")
+            .args(["--user", "--map-root-user", "true"])
+            .stdin(std::process::Stdio::null())
+            .stdout(std::process::Stdio::null())
+            .stderr(std::process::Stdio::null())
+            .status()
+            .map(|s| s.success())
+            .unwrap_or(false)
+    })
+}
+
 #[cfg(test)]
 mod tests {
     use super::{