From 277fdda89433b28f451ed9acea3012541eafba2a Mon Sep 17 00:00:00 2001 From: linkst <2024023709@m.scnu.edu.cn> Date: Fri, 31 Jul 2026 17:09:00 +0800 Subject: [PATCH] fix(sandbox): fall back to --map-auto when root-user mapping is restricted Plain `unshare --user --map-root-user` fails on kernels and containers that block unprivileged writes to /proc/self/uid_map (e.g. GitHub Actions, restricted AppArmor profiles). On those systems util-linux delegates to the setuid newuidmap/newgidmap helpers when --map-auto is also present. Add the combined form as a fallback candidate and build the launcher args from the probed mapping, so systems without newuidmap/newgidmap or a /etc/subuid range keep using the plain form. --- rust/crates/runtime/src/sandbox.rs | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/rust/crates/runtime/src/sandbox.rs b/rust/crates/runtime/src/sandbox.rs index 354cbfac..4eaaaf36 100644 --- a/rust/crates/runtime/src/sandbox.rs +++ b/rust/crates/runtime/src/sandbox.rs @@ -220,15 +220,18 @@ pub fn build_linux_sandbox_command( return None; } - let mut args = vec![ - "--user".to_string(), - "--map-root-user".to_string(), + let mut args: Vec = working_unshare_mapping() + .unwrap_or(UNSHARE_MAPPING_CANDIDATES[0]) + .iter() + .map(|arg| arg.to_string()) + .collect(); + args.extend([ "--mount".to_string(), "--ipc".to_string(), "--pid".to_string(), "--uts".to_string(), "--fork".to_string(), - ]; + ]); if status.network_active { args.push("--net".to_string()); } @@ -283,7 +286,16 @@ fn command_exists(command: &str) -> bool { } /// Candidate `unshare` user-namespace mapping options, in preference order. -const UNSHARE_MAPPING_CANDIDATES: &[&[&str]] = &[&["--user", "--map-root-user"]]; +/// +/// Most systems accept `--map-root-user` alone. On kernels or containers that +/// block unprivileged writes to `/proc/self/uid_map` (e.g. GitHub Actions, +/// restricted AppArmor profiles), util-linux instead delegates to the setuid +/// `newuidmap`/`newgidmap` helpers when `--map-auto` is also present; that +/// requires the current user to have a range in `/etc/subuid`/`/etc/subgid`. +const UNSHARE_MAPPING_CANDIDATES: &[&[&str]] = &[ + &["--user", "--map-root-user"], + &["--user", "--map-root-user", "--map-auto"], +]; /// Probe a candidate `unshare` mapping invocation with a trivial program. fn unshare_probe(args: &[&str]) -> bool { @@ -383,6 +395,21 @@ mod tests { assert_eq!(request.allowed_mounts, vec!["tmp"]); } + #[test] + fn mapping_candidates_prefer_plain_root_mapping() { + assert!(!super::UNSHARE_MAPPING_CANDIDATES.is_empty()); + for candidate in super::UNSHARE_MAPPING_CANDIDATES { + assert!(candidate.contains(&"--user")); + assert!(candidate.contains(&"--map-root-user")); + } + // The plain form must be tried first; `--map-auto` is only a fallback + // for kernels/containers that block unprivileged uid_map writes. + assert_eq!( + super::UNSHARE_MAPPING_CANDIDATES[0], + &["--user", "--map-root-user"] + ); + } + #[test] fn builds_linux_launcher_with_network_flag_when_requested() { let config = SandboxConfig::default();