diff --git a/src/runtime/cli/Arguments.rs b/src/runtime/cli/Arguments.rs index ffa6c47993f..f3f4bc7f428 100644 --- a/src/runtime/cli/Arguments.rs +++ b/src/runtime/cli/Arguments.rs @@ -813,6 +813,12 @@ pub fn parse(cmd: CommandTag, ctx: Context<'_>) -> Result::from(out_z.as_bytes()) } else { let mut temp = PathBuffer::uninit(); @@ -1565,6 +1571,99 @@ pub fn parse(cmd: CommandTag, ctx: Context<'_>) -> Result = Vec::with_capacity(4 + cwd.len() + 1); + entry.extend_from_slice(b"PWD="); + entry.extend_from_slice(cwd); + entry.push(0); + let entry_ptr: *mut core::ffi::c_char = Box::leak(entry.into_boxed_slice()).as_mut_ptr().cast(); + + // SAFETY: single-threaded argv parse; no other thread reads `environ` yet. + let env = unsafe { bun_core::os::environ() }; + for (i, slot) in env.iter().enumerate() { + // SAFETY: entries are NUL-terminated by construction in convert_env_to_wtf8. + let bytes = unsafe { bun_core::ffi::cstr_bytes(*slot as *const _) }; + if bytes.starts_with(b"PWD=") { + // SAFETY: `env.as_ptr()` is a `*const *mut c_char` into the Box::leak'd + // slice from convert_env_to_wtf8; we have exclusive access at argv-parse + // time (before any other thread exists). + unsafe { + let mut_base = env.as_ptr() as *mut *mut core::ffi::c_char; + *mut_base.add(i) = entry_ptr; + } + return; + } + } + + // No existing PWD entry — grow the envp slice by one. We rebuild the + // full NUL-terminated array (Box::leak'd) and swap it in via + // `set_environ`. + let mut new: Vec<*mut core::ffi::c_char> = Vec::with_capacity(env.len() + 2); + new.extend_from_slice(env); + new.push(entry_ptr); + new.push(core::ptr::null_mut()); + let new = Box::leak(new.into_boxed_slice()); + // SAFETY: single-threaded startup; `new` is live for the process lifetime. + unsafe { + bun_core::os::set_environ(new.as_mut_ptr(), new.len() - 1); + } +} + +#[cfg(not(windows))] +unsafe extern "C" { + fn setenv( + name: *const core::ffi::c_char, + value: *const core::ffi::c_char, + overwrite: core::ffi::c_int, + ) -> core::ffi::c_int; +} + +#[cfg(windows)] +unsafe extern "C" { + fn SetEnvironmentVariableW(name: *const u16, value: *const u16) -> i32; +} + /// Cold path: `bun test` option-group parsing — timeout / coverage / reporter / /// shard / parallel / seed / etc. Split out of [`parse`] so the `bun run