88// option. This file may not be copied, modified, or distributed
99// except according to those terms.
1010
11- use env;
12- use ffi:: CString ;
1311use io:: { self , Error , ErrorKind } ;
1412use libc:: { self , c_int, gid_t, pid_t, uid_t} ;
1513use ptr;
16-
1714use sys:: cvt;
1815use sys:: process:: process_common:: * ;
16+ use sys;
1917
2018////////////////////////////////////////////////////////////////////////////////
2119// Command
@@ -24,8 +22,6 @@ use sys::process::process_common::*;
2422impl Command {
2523 pub fn spawn ( & mut self , default : Stdio , needs_stdin : bool )
2624 -> io:: Result < ( Process , StdioPipes ) > {
27- use sys;
28-
2925 const CLOEXEC_MSG_FOOTER : & ' static [ u8 ] = b"NOEX" ;
3026
3127 let envp = self . capture_env ( ) ;
@@ -41,15 +37,26 @@ impl Command {
4137 return Ok ( ( ret, ours) )
4238 }
4339
44- let possible_paths = self . compute_possible_paths ( envp. as_ref ( ) ) ;
45-
4640 let ( input, output) = sys:: pipe:: anon_pipe ( ) ?;
4741
42+ // Whatever happens after the fork is almost for sure going to touch or
43+ // look at the environment in one way or another (PATH in `execvp` or
44+ // accessing the `environ` pointer ourselves). Make sure no other thread
45+ // is accessing the environment when we do the fork itself.
46+ //
47+ // Note that as soon as we're done with the fork there's no need to hold
48+ // a lock any more because the parent won't do anything and the child is
49+ // in its own process.
50+ let result = unsafe {
51+ let _env_lock = sys:: os:: env_lock ( ) ;
52+ cvt ( libc:: fork ( ) ) ?
53+ } ;
54+
4855 let pid = unsafe {
49- match cvt ( libc :: fork ( ) ) ? {
56+ match result {
5057 0 => {
5158 drop ( input) ;
52- let err = self . do_exec ( theirs, envp. as_ref ( ) , possible_paths ) ;
59+ let err = self . do_exec ( theirs, envp. as_ref ( ) ) ;
5360 let errno = err. raw_os_error ( ) . unwrap_or ( libc:: EINVAL ) as u32 ;
5461 let bytes = [
5562 ( errno >> 24 ) as u8 ,
@@ -117,46 +124,19 @@ impl Command {
117124 "nul byte found in provided data" )
118125 }
119126
120- let possible_paths = self . compute_possible_paths ( envp. as_ref ( ) ) ;
121127 match self . setup_io ( default, true ) {
122- Ok ( ( _, theirs) ) => unsafe { self . do_exec ( theirs, envp. as_ref ( ) , possible_paths) } ,
123- Err ( e) => e,
124- }
125- }
128+ Ok ( ( _, theirs) ) => {
129+ unsafe {
130+ // Similar to when forking, we want to ensure that access to
131+ // the environment is synchronized, so make sure to grab the
132+ // environment lock before we try to exec.
133+ let _lock = sys:: os:: env_lock ( ) ;
126134
127- fn compute_possible_paths ( & self , maybe_envp : Option < & CStringArray > ) -> Option < Vec < CString > > {
128- let program = self . get_program ( ) . as_bytes ( ) ;
129- if program. contains ( & b'/' ) {
130- return None ;
131- }
132- // Outside the match so we can borrow it for the lifetime of the function.
133- let parent_path = env:: var ( "PATH" ) . ok ( ) ;
134- let paths = match maybe_envp {
135- Some ( envp) => {
136- match envp. get_items ( ) . iter ( ) . find ( |var| var. as_bytes ( ) . starts_with ( b"PATH=" ) ) {
137- Some ( p) => & p. as_bytes ( ) [ 5 ..] ,
138- None => return None ,
139- }
140- } ,
141- // maybe_envp is None if the process isn't changing the parent's env at all.
142- None => {
143- match parent_path. as_ref ( ) {
144- Some ( p) => p. as_bytes ( ) ,
145- None => return None ,
135+ self . do_exec ( theirs, envp. as_ref ( ) )
146136 }
147- } ,
148- } ;
149-
150- let mut possible_paths = vec ! [ ] ;
151- for path in paths. split ( |p| * p == b':' ) {
152- let mut binary_path = Vec :: with_capacity ( program. len ( ) + path. len ( ) + 1 ) ;
153- binary_path. extend_from_slice ( path) ;
154- binary_path. push ( b'/' ) ;
155- binary_path. extend_from_slice ( program) ;
156- let c_binary_path = CString :: new ( binary_path) . unwrap ( ) ;
157- possible_paths. push ( c_binary_path) ;
137+ }
138+ Err ( e) => e,
158139 }
159- return Some ( possible_paths) ;
160140 }
161141
162142 // And at this point we've reached a special time in the life of the
@@ -192,8 +172,7 @@ impl Command {
192172 unsafe fn do_exec (
193173 & mut self ,
194174 stdio : ChildPipes ,
195- maybe_envp : Option < & CStringArray > ,
196- maybe_possible_paths : Option < Vec < CString > > ,
175+ maybe_envp : Option < & CStringArray >
197176 ) -> io:: Error {
198177 use sys:: { self , cvt_r} ;
199178
@@ -269,53 +248,29 @@ impl Command {
269248 t ! ( callback( ) ) ;
270249 }
271250
272- // If the program isn't an absolute path, and our environment contains a PATH var, then we
273- // implement the PATH traversal ourselves so that it honors the child's PATH instead of the
274- // parent's. This mirrors the logic that exists in glibc's execvpe, except using the
275- // child's env to fetch PATH.
276- match maybe_possible_paths {
277- Some ( possible_paths) => {
278- let mut pending_error = None ;
279- for path in possible_paths {
280- libc:: execve (
281- path. as_ptr ( ) ,
282- self . get_argv ( ) . as_ptr ( ) ,
283- maybe_envp. map ( |envp| envp. as_ptr ( ) ) . unwrap_or_else ( || * sys:: os:: environ ( ) )
284- ) ;
285- let err = io:: Error :: last_os_error ( ) ;
286- match err. kind ( ) {
287- io:: ErrorKind :: PermissionDenied => {
288- // If we saw a PermissionDenied, and none of the other entries in
289- // $PATH are successful, then we'll return the first EACCESS we see.
290- if pending_error. is_none ( ) {
291- pending_error = Some ( err) ;
292- }
293- } ,
294- // Errors which indicate we failed to find a file are ignored and we try
295- // the next entry in the path.
296- io:: ErrorKind :: NotFound | io:: ErrorKind :: TimedOut => {
297- continue
298- } ,
299- // Any other error means we found a file and couldn't execute it.
300- _ => {
301- return err;
302- }
251+ // Although we're performing an exec here we may also return with an
252+ // error from this function (without actually exec'ing) in which case we
253+ // want to be sure to restore the global environment back to what it
254+ // once was, ensuring that our temporary override, when free'd, doesn't
255+ // corrupt our process's environment.
256+ let mut _reset = None ;
257+ if let Some ( envp) = maybe_envp {
258+ struct Reset ( * const * const libc:: c_char ) ;
259+
260+ impl Drop for Reset {
261+ fn drop ( & mut self ) {
262+ unsafe {
263+ * sys:: os:: environ ( ) = self . 0 ;
303264 }
304265 }
305- if let Some ( err) = pending_error {
306- return err;
307- }
308- return io:: Error :: from_raw_os_error ( libc:: ENOENT ) ;
309- } ,
310- _ => {
311- libc:: execve (
312- self . get_argv ( ) [ 0 ] ,
313- self . get_argv ( ) . as_ptr ( ) ,
314- maybe_envp. map ( |envp| envp. as_ptr ( ) ) . unwrap_or_else ( || * sys:: os:: environ ( ) )
315- ) ;
316- return io:: Error :: last_os_error ( )
317266 }
267+
268+ _reset = Some ( Reset ( * sys:: os:: environ ( ) ) ) ;
269+ * sys:: os:: environ ( ) = envp. as_ptr ( ) ;
318270 }
271+
272+ libc:: execvp ( self . get_argv ( ) [ 0 ] , self . get_argv ( ) . as_ptr ( ) ) ;
273+ io:: Error :: last_os_error ( )
319274 }
320275
321276 #[ cfg( not( any( target_os = "macos" , target_os = "freebsd" ,
@@ -413,6 +368,8 @@ impl Command {
413368 libc:: POSIX_SPAWN_SETSIGMASK ;
414369 cvt ( libc:: posix_spawnattr_setflags ( & mut attrs. 0 , flags as _ ) ) ?;
415370
371+ // Make sure we synchronize access to the global `environ` resource
372+ let _env_lock = sys:: os:: env_lock ( ) ;
416373 let envp = envp. map ( |c| c. as_ptr ( ) )
417374 . unwrap_or_else ( || * sys:: os:: environ ( ) as * const _ ) ;
418375 let ret = libc:: posix_spawnp (
0 commit comments