8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use env;
12
- use ffi:: CString ;
13
11
use io:: { self , Error , ErrorKind } ;
14
12
use libc:: { self , c_int, gid_t, pid_t, uid_t} ;
15
13
use ptr;
16
-
17
14
use sys:: cvt;
18
15
use sys:: process:: process_common:: * ;
16
+ use sys;
19
17
20
18
////////////////////////////////////////////////////////////////////////////////
21
19
// Command
@@ -24,8 +22,6 @@ use sys::process::process_common::*;
24
22
impl Command {
25
23
pub fn spawn ( & mut self , default : Stdio , needs_stdin : bool )
26
24
-> io:: Result < ( Process , StdioPipes ) > {
27
- use sys;
28
-
29
25
const CLOEXEC_MSG_FOOTER : & ' static [ u8 ] = b"NOEX" ;
30
26
31
27
let envp = self . capture_env ( ) ;
@@ -41,15 +37,26 @@ impl Command {
41
37
return Ok ( ( ret, ours) )
42
38
}
43
39
44
- let possible_paths = self . compute_possible_paths ( envp. as_ref ( ) ) ;
45
-
46
40
let ( input, output) = sys:: pipe:: anon_pipe ( ) ?;
47
41
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
+
48
55
let pid = unsafe {
49
- match cvt ( libc :: fork ( ) ) ? {
56
+ match result {
50
57
0 => {
51
58
drop ( input) ;
52
- let err = self . do_exec ( theirs, envp. as_ref ( ) , possible_paths ) ;
59
+ let err = self . do_exec ( theirs, envp. as_ref ( ) ) ;
53
60
let errno = err. raw_os_error ( ) . unwrap_or ( libc:: EINVAL ) as u32 ;
54
61
let bytes = [
55
62
( errno >> 24 ) as u8 ,
@@ -117,46 +124,19 @@ impl Command {
117
124
"nul byte found in provided data" )
118
125
}
119
126
120
- let possible_paths = self . compute_possible_paths ( envp. as_ref ( ) ) ;
121
127
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 ( ) ;
126
134
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 ( ) )
146
136
}
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,
158
139
}
159
- return Some ( possible_paths) ;
160
140
}
161
141
162
142
// And at this point we've reached a special time in the life of the
@@ -192,8 +172,7 @@ impl Command {
192
172
unsafe fn do_exec (
193
173
& mut self ,
194
174
stdio : ChildPipes ,
195
- maybe_envp : Option < & CStringArray > ,
196
- maybe_possible_paths : Option < Vec < CString > > ,
175
+ maybe_envp : Option < & CStringArray >
197
176
) -> io:: Error {
198
177
use sys:: { self , cvt_r} ;
199
178
@@ -269,53 +248,29 @@ impl Command {
269
248
t ! ( callback( ) ) ;
270
249
}
271
250
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 ;
303
264
}
304
265
}
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 ( )
317
266
}
267
+
268
+ _reset = Some ( Reset ( * sys:: os:: environ ( ) ) ) ;
269
+ * sys:: os:: environ ( ) = envp. as_ptr ( ) ;
318
270
}
271
+
272
+ libc:: execvp ( self . get_argv ( ) [ 0 ] , self . get_argv ( ) . as_ptr ( ) ) ;
273
+ io:: Error :: last_os_error ( )
319
274
}
320
275
321
276
#[ cfg( not( any( target_os = "macos" , target_os = "freebsd" ,
@@ -413,6 +368,8 @@ impl Command {
413
368
libc:: POSIX_SPAWN_SETSIGMASK ;
414
369
cvt ( libc:: posix_spawnattr_setflags ( & mut attrs. 0 , flags as _ ) ) ?;
415
370
371
+ // Make sure we synchronize access to the global `environ` resource
372
+ let _env_lock = sys:: os:: env_lock ( ) ;
416
373
let envp = envp. map ( |c| c. as_ptr ( ) )
417
374
. unwrap_or_else ( || * sys:: os:: environ ( ) as * const _ ) ;
418
375
let ret = libc:: posix_spawnp (
0 commit comments