From 3a8c5642d283e26b089ae31cf80044c68ecaeeff Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Sun, 12 Apr 2026 14:41:54 -0700 Subject: [PATCH] fix: protect active arg0 helper dirs Co-authored-by: Codex noreply@openai.com --- codex-rs/arg0/src/lib.rs | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs index f8b61796b5..4b6aefce10 100644 --- a/codex-rs/arg0/src/lib.rs +++ b/codex-rs/arg0/src/lib.rs @@ -15,6 +15,7 @@ const MISSPELLED_APPLY_PATCH_ARG0: &str = "applypatch"; #[cfg(unix)] const EXECVE_WRAPPER_ARG0: &str = "codex-execve-wrapper"; const LOCK_FILENAME: &str = ".lock"; +const PID_FILENAME: &str = ".pid"; const TOKIO_WORKER_STACK_SIZE_BYTES: usize = 16 * 1024 * 1024; #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -296,6 +297,7 @@ pub fn prepend_path_entry_for_codex_aliases() -> std::io::Result std::io::Result<()> { continue; } + if dir_belongs_to_running_process(&path) { + continue; + } + // Skip the directory if locking fails or the lock is currently held. let Some(_lock_file) = try_lock_dir(&path)? else { continue; @@ -415,6 +421,36 @@ fn janitor_cleanup(temp_root: &Path) -> std::io::Result<()> { Ok(()) } +fn write_owner_pid(dir: &Path) -> std::io::Result<()> { + std::fs::write(dir.join(PID_FILENAME), format!("{}\n", std::process::id())) +} + +fn dir_belongs_to_running_process(dir: &Path) -> bool { + let Ok(pid) = std::fs::read_to_string(dir.join(PID_FILENAME)) else { + return false; + }; + let Ok(pid) = pid.trim().parse::() else { + return false; + }; + owner_pid_is_running(pid) +} + +fn owner_pid_is_running(pid: u32) -> bool { + if pid == std::process::id() { + return true; + } + + #[cfg(target_os = "linux")] + { + Path::new("/proc").join(pid.to_string()).exists() + } + + #[cfg(not(target_os = "linux"))] + { + false + } +} + fn try_lock_dir(dir: &Path) -> std::io::Result> { let lock_path = dir.join(LOCK_FILENAME); let lock_file = match File::options().read(true).write(true).open(&lock_path) { @@ -435,6 +471,7 @@ mod tests { use super::Arg0DispatchPaths; use super::Arg0PathEntryGuard; use super::LOCK_FILENAME; + use super::PID_FILENAME; use super::janitor_cleanup; use super::linux_sandbox_exe_path; use std::fs; @@ -453,6 +490,10 @@ mod tests { .open(lock_path) } + fn write_pid(dir: &Path, pid: u32) -> std::io::Result<()> { + fs::write(dir.join(PID_FILENAME), format!("{pid}\n")) + } + #[test] fn linux_sandbox_exe_path_prefers_codex_linux_sandbox_alias() -> std::io::Result<()> { let temp_dir = TempDir::new()?; @@ -501,6 +542,34 @@ mod tests { Ok(()) } + #[test] + fn janitor_skips_dirs_owned_by_running_process() -> std::io::Result<()> { + let root = tempfile::tempdir()?; + let dir = root.path().join("active-pid"); + fs::create_dir(&dir)?; + create_lock(&dir)?; + write_pid(&dir, std::process::id())?; + + janitor_cleanup(root.path())?; + + assert!(dir.exists()); + Ok(()) + } + + #[test] + fn janitor_removes_dirs_with_stale_pid_and_unlocked_lock() -> std::io::Result<()> { + let root = tempfile::tempdir()?; + let dir = root.path().join("stale-pid"); + fs::create_dir(&dir)?; + create_lock(&dir)?; + write_pid(&dir, u32::MAX)?; + + janitor_cleanup(root.path())?; + + assert!(!dir.exists()); + Ok(()) + } + #[test] fn janitor_removes_dirs_with_unlocked_lock() -> std::io::Result<()> { let root = tempfile::tempdir()?;