diff --git a/codex-rs/sandboxing/src/seatbelt_base_policy.sbpl b/codex-rs/sandboxing/src/seatbelt_base_policy.sbpl index ba3140aa44..2fca6a886e 100644 --- a/codex-rs/sandboxing/src/seatbelt_base_policy.sbpl +++ b/codex-rs/sandboxing/src/seatbelt_base_policy.sbpl @@ -64,6 +64,8 @@ (sysctl-name "kern.osvariant_status") (sysctl-name "kern.osversion") (sysctl-name "kern.secure_kernel") + ; Python's ProcessPoolExecutor queries this through sysconf(_SC_SEM_NSEMS_MAX). + (sysctl-name "kern.sysv.semmns") (sysctl-name "kern.usrstack64") (sysctl-name "kern.version") (sysctl-name "sysctl.proc_cputype") diff --git a/codex-rs/sandboxing/src/seatbelt_tests.rs b/codex-rs/sandboxing/src/seatbelt_tests.rs index e9e42f054a..ce8fae2a56 100644 --- a/codex-rs/sandboxing/src/seatbelt_tests.rs +++ b/codex-rs/sandboxing/src/seatbelt_tests.rs @@ -136,6 +136,41 @@ fn base_policy_allows_node_cpu_sysctls() { ); } +#[test] +fn seatbelt_allows_semaphore_limit_sysconf() { + let workspace = TempDir::new().expect("temp workspace"); + for policy in [ + SandboxPolicy::new_read_only_policy(), + SandboxPolicy::new_workspace_write_policy(), + ] { + // getconf calls the same sysconf used by Python's ProcessPoolExecutor. + let args = create_seatbelt_command_args_for_legacy_policy( + vec!["/usr/bin/getconf".to_string(), "SEM_NSEMS_MAX".to_string()], + &policy, + workspace.path(), + /*enforce_managed_network*/ false, + /*network*/ None, + ) + .expect("create seatbelt args"); + let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE) + .args(args) + .current_dir(workspace.path()) + .output() + .expect("execute semaphore limit query under seatbelt"); + let stderr = String::from_utf8_lossy(&output.stderr); + if !output.status.success() + && stderr.contains("sandbox-exec: sandbox_apply: Operation not permitted") + { + eprintln!("skipping semaphore limit query: nested Seatbelt is unavailable"); + return; + } + assert!( + output.status.success(), + "semaphore limit query should succeed under {policy:?}: {stderr}" + ); + } +} + #[test] fn base_policy_allows_kmp_registration_shm_read_create_and_unlink() { let expected = r##"(allow ipc-posix-shm-read-data