diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 164e8a085774..df05e82f3a48 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2801,6 +2801,7 @@ dependencies = [ "cc", "clap", "codex-core", + "codex-process-hardening", "codex-protocol", "codex-sandboxing", "codex-utils-absolute-path", diff --git a/codex-rs/linux-sandbox/Cargo.toml b/codex-rs/linux-sandbox/Cargo.toml index 519ae5138e11..05967661e263 100644 --- a/codex-rs/linux-sandbox/Cargo.toml +++ b/codex-rs/linux-sandbox/Cargo.toml @@ -17,6 +17,7 @@ workspace = true [target.'cfg(target_os = "linux")'.dependencies] clap = { workspace = true, features = ["derive"] } +codex-process-hardening = { workspace = true } codex-protocol = { workspace = true } codex-sandboxing = { workspace = true } codex-utils-absolute-path = { workspace = true } diff --git a/codex-rs/linux-sandbox/src/landlock.rs b/codex-rs/linux-sandbox/src/landlock.rs index 5794982530e6..50ea87bd1df9 100644 --- a/codex-rs/linux-sandbox/src/landlock.rs +++ b/codex-rs/linux-sandbox/src/landlock.rs @@ -176,6 +176,8 @@ fn install_network_seccomp_filter_on_current_thread( let mut rules: BTreeMap> = BTreeMap::new(); deny_syscall(&mut rules, libc::SYS_ptrace); + deny_syscall(&mut rules, libc::SYS_process_vm_readv); + deny_syscall(&mut rules, libc::SYS_process_vm_writev); deny_syscall(&mut rules, libc::SYS_io_uring_setup); deny_syscall(&mut rules, libc::SYS_io_uring_enter); deny_syscall(&mut rules, libc::SYS_io_uring_register); diff --git a/codex-rs/linux-sandbox/src/proxy_routing.rs b/codex-rs/linux-sandbox/src/proxy_routing.rs index 07e1893ee3d7..d28b8646660c 100644 --- a/codex-rs/linux-sandbox/src/proxy_routing.rs +++ b/codex-rs/linux-sandbox/src/proxy_routing.rs @@ -450,7 +450,7 @@ fn spawn_host_bridge(endpoint: SocketAddr, uds_path: &Path) -> io::Result io::Result<()> { - set_parent_death_signal()?; + harden_bridge_process()?; if uds_path.exists() { std::fs::remove_file(uds_path)?; } @@ -501,7 +501,7 @@ fn spawn_local_bridge(uds_path: &Path) -> io::Result { } fn run_local_bridge(uds_path: &Path, ready_fd: libc::c_int) -> io::Result<()> { - set_parent_death_signal()?; + harden_bridge_process()?; let listener = bind_local_loopback_listener()?; let port = listener.local_addr()?.port(); @@ -614,6 +614,11 @@ fn set_parent_death_signal() -> io::Result<()> { } } +fn harden_bridge_process() -> io::Result<()> { + set_parent_death_signal()?; + codex_process_hardening::disable_process_dumping() +} + fn proxy_bidirectional(mut tcp_stream: TcpStream, mut unix_stream: UnixStream) -> io::Result<()> { let mut tcp_reader = tcp_stream.try_clone()?; let mut unix_writer = unix_stream.try_clone()?; diff --git a/codex-rs/process-hardening/src/lib.rs b/codex-rs/process-hardening/src/lib.rs index 1c206aae8751..f9695fcbdc72 100644 --- a/codex-rs/process-hardening/src/lib.rs +++ b/codex-rs/process-hardening/src/lib.rs @@ -61,6 +61,17 @@ pub(crate) fn pre_main_hardening_linux() { remove_env_vars_with_prefix(b"LD_"); } +/// Mark the current Linux process non-dumpable so same-user processes cannot attach with ptrace. +#[cfg(target_os = "linux")] +pub fn disable_process_dumping() -> std::io::Result<()> { + let ret_code = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0, 0, 0, 0) }; + if ret_code == 0 { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] pub(crate) fn pre_main_hardening_bsd() { // FreeBSD/OpenBSD: set RLIMIT_CORE to 0 and clear LD_* env vars