The high-order bit on this PR is that it makes it so `sandbox.rs` tests both Mac and Linux, as we introduce a general `spawn_command_under_sandbox()` function with platform-specific implementations for testing. An important, and interesting, discovery in porting the test to Linux is that (for reasons cited in the code comments), `/dev/shm` has to be added to `writable_roots` on Linux in order for `multiprocessing.Lock` to work there. Granting write access to `/dev/shm` comes with some degree of risk, so we do not make this the default for Codex CLI. Piggybacking on top of #2317, this moves the `python_multiprocessing_lock_works` test yet again, moving `codex-rs/core/tests/sandbox.rs` to `codex-rs/exec/tests/sandbox.rs` because in `codex-rs/exec/tests` we can use `cargo_bin()` like so: ``` let codex_linux_sandbox_exe = assert_cmd::cargo::cargo_bin("codex-exec"); ``` which is necessary so we can use `codex_linux_sandbox_exe` and therefore `spawn_command_under_linux_sandbox` in an integration test. This also moves `spawn_command_under_linux_sandbox()` out of `exec.rs` and into `landlock.rs`, which makes things more consistent with `seatbelt.rs` in `codex-core`. For reference, https://github.com/openai/codex/pull/1808 is the PR that made the change to Seatbelt to get this test to pass on Mac.
93 lines
2.6 KiB
Rust
93 lines
2.6 KiB
Rust
#![cfg(unix)]
|
|
#![expect(clippy::expect_used)]
|
|
|
|
use codex_core::protocol::SandboxPolicy;
|
|
use codex_core::spawn::StdioPolicy;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
use tokio::process::Child;
|
|
|
|
#[cfg(target_os = "macos")]
|
|
async fn spawn_command_under_sandbox(
|
|
command: Vec<String>,
|
|
sandbox_policy: &SandboxPolicy,
|
|
cwd: PathBuf,
|
|
stdio_policy: StdioPolicy,
|
|
env: HashMap<String, String>,
|
|
) -> std::io::Result<Child> {
|
|
use codex_core::seatbelt::spawn_command_under_seatbelt;
|
|
spawn_command_under_seatbelt(command, sandbox_policy, cwd, stdio_policy, env).await
|
|
}
|
|
|
|
#[cfg(target_os = "linux")]
|
|
async fn spawn_command_under_sandbox(
|
|
command: Vec<String>,
|
|
sandbox_policy: &SandboxPolicy,
|
|
cwd: PathBuf,
|
|
stdio_policy: StdioPolicy,
|
|
env: HashMap<String, String>,
|
|
) -> std::io::Result<Child> {
|
|
use codex_core::landlock::spawn_command_under_linux_sandbox;
|
|
let codex_linux_sandbox_exe = assert_cmd::cargo::cargo_bin("codex-exec");
|
|
spawn_command_under_linux_sandbox(
|
|
codex_linux_sandbox_exe,
|
|
command,
|
|
sandbox_policy,
|
|
cwd,
|
|
stdio_policy,
|
|
env,
|
|
)
|
|
.await
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn python_multiprocessing_lock_works_under_sandbox() {
|
|
#[cfg(target_os = "macos")]
|
|
let writable_roots = Vec::<PathBuf>::new();
|
|
|
|
// From https://man7.org/linux/man-pages/man7/sem_overview.7.html
|
|
//
|
|
// > On Linux, named semaphores are created in a virtual filesystem,
|
|
// > normally mounted under /dev/shm.
|
|
#[cfg(target_os = "linux")]
|
|
let writable_roots = vec![PathBuf::from("/dev/shm")];
|
|
|
|
let policy = SandboxPolicy::WorkspaceWrite {
|
|
writable_roots,
|
|
network_access: false,
|
|
exclude_tmpdir_env_var: false,
|
|
exclude_slash_tmp: false,
|
|
};
|
|
|
|
let python_code = r#"import multiprocessing
|
|
from multiprocessing import Lock, Process
|
|
|
|
def f(lock):
|
|
with lock:
|
|
print("Lock acquired in child process")
|
|
|
|
if __name__ == '__main__':
|
|
lock = Lock()
|
|
p = Process(target=f, args=(lock,))
|
|
p.start()
|
|
p.join()
|
|
"#;
|
|
|
|
let mut child = spawn_command_under_sandbox(
|
|
vec![
|
|
"python3".to_string(),
|
|
"-c".to_string(),
|
|
python_code.to_string(),
|
|
],
|
|
&policy,
|
|
std::env::current_dir().expect("should be able to get current dir"),
|
|
StdioPolicy::Inherit,
|
|
HashMap::new(),
|
|
)
|
|
.await
|
|
.expect("should be able to spawn python under sandbox");
|
|
|
|
let status = child.wait().await.expect("should wait for child process");
|
|
assert!(status.success(), "python exited with {status:?}");
|
|
}
|