|
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; |
@@ -41,15 +39,13 @@ impl Command { |
41 | 39 | return Ok((ret, ours)) |
42 | 40 | } |
43 | 41 |
|
44 | | - let possible_paths = self.compute_possible_paths(envp.as_ref()); |
45 | | - |
46 | 42 | let (input, output) = sys::pipe::anon_pipe()?; |
47 | 43 |
|
48 | 44 | let pid = unsafe { |
49 | 45 | match cvt(libc::fork())? { |
50 | 46 | 0 => { |
51 | 47 | drop(input); |
52 | | - let err = self.do_exec(theirs, envp.as_ref(), possible_paths); |
| 48 | + let err = self.do_exec(theirs, envp.as_ref()); |
53 | 49 | let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; |
54 | 50 | let bytes = [ |
55 | 51 | (errno >> 24) as u8, |
@@ -117,48 +113,12 @@ impl Command { |
117 | 113 | "nul byte found in provided data") |
118 | 114 | } |
119 | 115 |
|
120 | | - let possible_paths = self.compute_possible_paths(envp.as_ref()); |
121 | 116 | match self.setup_io(default, true) { |
122 | | - Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref(), possible_paths) }, |
| 117 | + Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) }, |
123 | 118 | Err(e) => e, |
124 | 119 | } |
125 | 120 | } |
126 | 121 |
|
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, |
146 | | - } |
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); |
158 | | - } |
159 | | - return Some(possible_paths); |
160 | | - } |
161 | | - |
162 | 122 | // And at this point we've reached a special time in the life of the |
163 | 123 | // child. The child must now be considered hamstrung and unable to |
164 | 124 | // do anything other than syscalls really. Consider the following |
@@ -192,8 +152,7 @@ impl Command { |
192 | 152 | unsafe fn do_exec( |
193 | 153 | &mut self, |
194 | 154 | stdio: ChildPipes, |
195 | | - maybe_envp: Option<&CStringArray>, |
196 | | - maybe_possible_paths: Option<Vec<CString>>, |
| 155 | + maybe_envp: Option<&CStringArray> |
197 | 156 | ) -> io::Error { |
198 | 157 | use sys::{self, cvt_r}; |
199 | 158 |
|
@@ -269,53 +228,32 @@ impl Command { |
269 | 228 | t!(callback()); |
270 | 229 | } |
271 | 230 |
|
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 | | - } |
| 231 | + // Note that we're accessing process-global state, `environ`, which |
| 232 | + // means we need the rust-specific environment lock. Although we're |
| 233 | + // performing an exec here we may also return with an error from this |
| 234 | + // function (without actually exec'ing) in which case we want to be sure |
| 235 | + // to restore the global environment back to what it once was, ensuring |
| 236 | + // that our temporary override, when free'd, doesn't corrupt our |
| 237 | + // process's environment. |
| 238 | + let _lock = sys::os::env_lock(); |
| 239 | + let mut _reset = None; |
| 240 | + if let Some(envp) = maybe_envp { |
| 241 | + struct Reset(*const *const libc::c_char); |
| 242 | + |
| 243 | + impl Drop for Reset { |
| 244 | + fn drop(&mut self) { |
| 245 | + unsafe { |
| 246 | + *sys::os::environ() = self.0; |
303 | 247 | } |
304 | 248 | } |
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 | 249 | } |
| 250 | + |
| 251 | + _reset = Some(Reset(*sys::os::environ())); |
| 252 | + *sys::os::environ() = envp.as_ptr(); |
318 | 253 | } |
| 254 | + |
| 255 | + libc::execvp(self.get_argv()[0], self.get_argv().as_ptr()); |
| 256 | + io::Error::last_os_error() |
319 | 257 | } |
320 | 258 |
|
321 | 259 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", |
@@ -413,6 +351,10 @@ impl Command { |
413 | 351 | libc::POSIX_SPAWN_SETSIGMASK; |
414 | 352 | cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?; |
415 | 353 |
|
| 354 | + // We'e reading `sys::os::environ` below so make sure that we do so |
| 355 | + // in a synchronized fashion via the rust-specific global |
| 356 | + // environment lock. |
| 357 | + let _lock = sys::os::env_lock(); |
416 | 358 | let envp = envp.map(|c| c.as_ptr()) |
417 | 359 | .unwrap_or_else(|| *sys::os::environ() as *const _); |
418 | 360 | let ret = libc::posix_spawnp( |
|
0 commit comments