seatbelt: allow openpty() (#7507)

This allows `openpty(3)` to run in the default sandbox. Also permit
reading `kern.argmax`, which is the maximum number of arguments to
exec().
This commit is contained in:
Jeremy Rose
2025-12-03 09:15:38 -08:00
committed by GitHub
Unverified
parent 45f3250eec
commit 9b3251f28f
3 changed files with 57 additions and 10 deletions
+11 -2
View File
@@ -53,6 +53,7 @@
(sysctl-name "hw.physicalcpu_max")
(sysctl-name "hw.tbfrequency_compat")
(sysctl-name "hw.vectorunit")
(sysctl-name "kern.argmax")
(sysctl-name "kern.hostname")
(sysctl-name "kern.maxfilesperproc")
(sysctl-name "kern.maxproc")
@@ -72,7 +73,8 @@
(sysctl-name-prefix "net.routetable.")
)
; Allow Java to set CPU type grade when required
; Allow Java to read some CPU info. This is misclassified as a "write" because
; userspace passes a memory buffer to the sysctl, but conceptually it is a read.
(allow sysctl-write
(sysctl-name "kern.grade_cputype"))
@@ -86,10 +88,17 @@
(global-name "com.apple.system.opendirectoryd.libinfo")
)
; Added on top of Chrome profile
; Needed for python multiprocessing on MacOS for the SemLock
(allow ipc-posix-sem)
(allow mach-lookup
(global-name "com.apple.PowerManagement.control")
)
; allow openpty()
(allow pseudo-tty)
(allow file-read* file-write* file-ioctl (literal "/dev/ptmx"))
(allow file-read* file-write*
(require-all
(regex #"^/dev/ttys[0-9]+")
(extension "com.apple.sandbox.pty")))
+7 -8
View File
@@ -159,23 +159,18 @@ async fn read_only_forbids_all_writes() {
.await;
}
/// Verify that user lookups via `pwd.getpwuid(os.getuid())` work under the
/// seatbelt sandbox. Prior to allowing the necessary machlookup for
/// OpenDirectory libinfo, this would fail with `KeyError: getpwuid(): uid not found`.
#[tokio::test]
async fn python_getpwuid_works_under_seatbelt() {
async fn openpty_works_under_seatbelt() {
if std::env::var(CODEX_SANDBOX_ENV_VAR) == Ok("seatbelt".to_string()) {
eprintln!("{CODEX_SANDBOX_ENV_VAR} is set to 'seatbelt', skipping test.");
return;
}
// For local dev.
if which::which("python3").is_err() {
eprintln!("python3 not found in PATH, skipping test.");
return;
}
// ReadOnly is sufficient here since we are only exercising user lookup.
let policy = SandboxPolicy::ReadOnly;
let command_cwd = std::env::current_dir().expect("getcwd");
let sandbox_cwd = command_cwd.clone();
@@ -184,8 +179,12 @@ async fn python_getpwuid_works_under_seatbelt() {
vec![
"python3".to_string(),
"-c".to_string(),
// Print the passwd struct; success implies lookup worked.
"import pwd, os; print(pwd.getpwuid(os.getuid()))".to_string(),
r#"import os
master, slave = os.openpty()
os.write(slave, b"ping")
assert os.read(master, 4) == b"ping""#
.to_string(),
],
command_cwd,
&policy,
+39
View File
@@ -109,6 +109,45 @@ if __name__ == '__main__':
assert!(status.success(), "python exited with {status:?}");
}
#[tokio::test]
async fn python_getpwuid_works_under_sandbox() {
core_test_support::skip_if_sandbox!();
if std::process::Command::new("python3")
.arg("--version")
.status()
.is_err()
{
eprintln!("python3 not found in PATH, skipping test.");
return;
}
let policy = SandboxPolicy::ReadOnly;
let command_cwd = std::env::current_dir().expect("should be able to get current dir");
let sandbox_cwd = command_cwd.clone();
let mut child = spawn_command_under_sandbox(
vec![
"python3".to_string(),
"-c".to_string(),
"import pwd, os; print(pwd.getpwuid(os.getuid()))".to_string(),
],
command_cwd,
&policy,
sandbox_cwd.as_path(),
StdioPolicy::RedirectForShellTool,
HashMap::new(),
)
.await
.expect("should be able to spawn python under sandbox");
let status = child
.wait()
.await
.expect("should be able to wait for child process");
assert!(status.success(), "python exited with {status:?}");
}
#[tokio::test]
async fn sandbox_distinguishes_command_and_policy_cwds() {
core_test_support::skip_if_sandbox!();