|  | 
| 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