From 2fd13d40b24a56e1e14d411d1850a431087252d3 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Thu, 11 Dec 2025 01:41:07 +0000 Subject: [PATCH 1/2] optimise to_exec_array match the other implemention in: https://github.com/nix-rust/nix/blob/71f6ee9f4380d5caad3c049dbd352627b9d6eb0a/src/unistd.rs#L1082-L1088 --- src/spawn.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/spawn.rs b/src/spawn.rs index 34c8666bd2..0cc1ded9e8 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -351,12 +351,11 @@ impl Drop for PosixSpawnFileActions { // SAFETY: // It is safe to add the mutability in types as implementations won't mutable them. unsafe fn to_exec_array>(args: &[S]) -> Vec<*mut libc::c_char> { - let mut v: Vec<*mut libc::c_char> = args - .iter() + use std::iter::once; + args.iter() .map(|s| s.as_ref().as_ptr().cast_mut()) - .collect(); - v.push(std::ptr::null_mut()); - v + .chain(once(std::ptr::null_mut())) + .collect() } /// Create a new child process from the specified process image. See From c49d22e869304eba05c60d05f205927a59fde04c Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:42:14 +0000 Subject: [PATCH 2/2] refactor to_exec_array --- src/lib.rs | 11 ++++++++ src/spawn.rs | 73 ++++++++++++++++++++++++--------------------------- src/unistd.rs | 27 +++++++------------ 3 files changed, 55 insertions(+), 56 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b838a47617..fa63c2314c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -412,3 +412,14 @@ where None => Ok(f(ptr::null())), } } + +#[cfg(feature = "process")] +pub(crate) fn c_slice_to_pointers>( + slice: &[S], +) -> Box<[*const libc::c_char]> { + slice + .iter() + .map(|s| s.as_ref().as_ptr()) + .chain(std::iter::once(std::ptr::null())) + .collect() +} diff --git a/src/spawn.rs b/src/spawn.rs index 0cc1ded9e8..e6061de4ec 100644 --- a/src/spawn.rs +++ b/src/spawn.rs @@ -343,21 +343,6 @@ impl Drop for PosixSpawnFileActions { } } -// The POSIX standard requires those `args` and `envp` to be of type `*const *mut [c_char]`, -// but implementations won't modify them, making the `mut` type redundant. Considering this, -// Nix does not expose this mutability, but we have to change the interface when calling the -// underlying libc interfaces , this helper function does the conversion job. -// -// SAFETY: -// It is safe to add the mutability in types as implementations won't mutable them. -unsafe fn to_exec_array>(args: &[S]) -> Vec<*mut libc::c_char> { - use std::iter::once; - args.iter() - .map(|s| s.as_ref().as_ptr().cast_mut()) - .chain(once(std::ptr::null_mut())) - .collect() -} - /// Create a new child process from the specified process image. See /// [posix_spawn](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_spawn.html). pub fn posix_spawn( @@ -374,19 +359,24 @@ where { let mut pid = 0; - let ret = unsafe { - let args_p = to_exec_array(args); - let env_p = to_exec_array(envp); - + let ret = { path.with_nix_path(|c_str| { - libc::posix_spawn( - &mut pid as *mut libc::pid_t, - c_str.as_ptr(), - &file_actions.fa as *const libc::posix_spawn_file_actions_t, - &attr.attr as *const libc::posix_spawnattr_t, - args_p.as_ptr(), - env_p.as_ptr(), - ) + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(envp); + + // SAFETY: The POSIX standard specifies `argv` and `envp` as `*const *mut c_char`, + // but also states that these arrays and their strings shall not be modified, + // so passing immutable data is sound. + unsafe { + libc::posix_spawn( + &mut pid as *mut libc::pid_t, + c_str.as_ptr(), + &file_actions.fa as *const libc::posix_spawn_file_actions_t, + &attr.attr as *const libc::posix_spawnattr_t, + args_p.as_ptr() as *const *mut _, + env_p.as_ptr() as *const *mut _, + ) + } })? }; @@ -408,18 +398,23 @@ pub fn posix_spawnp, SE: AsRef>( ) -> Result { let mut pid = 0; - let ret = unsafe { - let args_p = to_exec_array(args); - let env_p = to_exec_array(envp); - - libc::posix_spawnp( - &mut pid as *mut libc::pid_t, - path.as_ptr(), - &file_actions.fa as *const libc::posix_spawn_file_actions_t, - &attr.attr as *const libc::posix_spawnattr_t, - args_p.as_ptr(), - env_p.as_ptr(), - ) + let ret = { + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(envp); + + // SAFETY: The POSIX standard specifies `argv` and `envp` as `*const *mut c_char`, + // but also states that these arrays and their strings shall not be modified, + // so passing immutable data is sound. + unsafe { + libc::posix_spawnp( + &mut pid as *mut libc::pid_t, + path.as_ptr(), + &file_actions.fa as *const libc::posix_spawn_file_actions_t, + &attr.attr as *const libc::posix_spawnattr_t, + args_p.as_ptr() as *const *mut _, + env_p.as_ptr() as *const *mut _, + ) + } }; if ret != 0 { diff --git a/src/unistd.rs b/src/unistd.rs index 7802fe7ff0..856192a9e0 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -1079,13 +1079,6 @@ pub fn fchownat( feature! { #![feature = "process"] -fn to_exec_array>(args: &[S]) -> Vec<*const c_char> { - use std::iter::once; - args.iter() - .map(|s| s.as_ref().as_ptr()) - .chain(once(ptr::null())) - .collect() -} /// Replace the current process image with a new one (see /// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)). @@ -1095,7 +1088,7 @@ fn to_exec_array>(args: &[S]) -> Vec<*const c_char> { /// environment for the new process. #[inline] pub fn execv>(path: &CStr, argv: &[S]) -> Result { - let args_p = to_exec_array(argv); + let args_p = crate::c_slice_to_pointers(argv); unsafe { libc::execv(path.as_ptr(), args_p.as_ptr()) }; @@ -1120,8 +1113,8 @@ pub fn execve, SE: AsRef>( args: &[SA], env: &[SE], ) -> Result { - let args_p = to_exec_array(args); - let env_p = to_exec_array(env); + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(env); unsafe { libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) }; @@ -1142,7 +1135,7 @@ pub fn execvp>( filename: &CStr, args: &[S], ) -> Result { - let args_p = to_exec_array(args); + let args_p = crate::c_slice_to_pointers(args); unsafe { libc::execvp(filename.as_ptr(), args_p.as_ptr()) }; @@ -1162,8 +1155,8 @@ pub fn execvpe, SE: AsRef>( args: &[SA], env: &[SE], ) -> Result { - let args_p = to_exec_array(args); - let env_p = to_exec_array(env); + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(env); unsafe { libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) @@ -1191,8 +1184,8 @@ pub fn fexecve, SE: AsRef>( ) -> Result { use std::os::fd::AsRawFd; - let args_p = to_exec_array(args); - let env_p = to_exec_array(env); + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(env); unsafe { libc::fexecve(fd.as_fd().as_raw_fd(), args_p.as_ptr(), env_p.as_ptr()) }; @@ -1220,8 +1213,8 @@ pub fn execveat, SE: AsRef>( ) -> Result { use std::os::fd::AsRawFd; - let args_p = to_exec_array(args); - let env_p = to_exec_array(env); + let args_p = crate::c_slice_to_pointers(args); + let env_p = crate::c_slice_to_pointers(env); unsafe { libc::syscall(