Rollup merge of #82949 - the8472:forget-envlock-on-fork, r=joshtriplett

Do not attempt to unlock envlock in child process after a fork.

This implements the first two points from https://github.com/rust-lang/rust/issues/64718#issuecomment-793030479

This is a breaking change for cases where the environment is accessed in a Command::pre_exec closure. Except for single-threaded programs these uses were not correct anyway since they aren't async-signal safe.

Note that we had a ui test that explicitly tried `env::set_var` in `pre_exec`. As expected it failed with these changes when I tested locally.
This commit is contained in:
Dylan DPC
2021-03-10 17:55:43 +01:00
committed by GitHub
3 changed files with 19 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
use crate::convert::TryInto;
use crate::fmt;
use crate::io::{self, Error, ErrorKind};
use crate::mem;
use crate::ptr;
use crate::sys;
use crate::sys::cvt;
@@ -45,15 +46,14 @@ impl Command {
//
// Note that as soon as we're done with the fork there's no need to hold
// a lock any more because the parent won't do anything and the child is
// in its own process.
let result = unsafe {
let _env_lock = sys::os::env_lock();
cvt(libc::fork())?
};
// in its own process. Thus the parent drops the lock guard while the child
// forgets it to avoid unlocking it on a new thread, which would be invalid.
let (env_lock, result) = unsafe { (sys::os::env_lock(), cvt(libc::fork())?) };
let pid = unsafe {
match result {
0 => {
mem::forget(env_lock);
drop(input);
let Err(err) = self.do_exec(theirs, envp.as_ref());
let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
@@ -74,7 +74,10 @@ impl Command {
rtassert!(output.write(&bytes).is_ok());
libc::_exit(1)
}
n => n,
n => {
drop(env_lock);
n
}
}
};