Skip to content

Commit 41308a0

Browse files
committed
Auto merge of #29305 - alexcrichton:bad-getenv, r=brson
As discovered in #29298, `env::set_var("", "")` will panic, but it turns out that it *also* deadlocks on Unix systems. This happens because if a panic happens while holding the environment lock, we then go try to read RUST_BACKTRACE, grabbing the environment lock, causing a deadlock. Specifically, the changes made here are: * The environment lock is pushed into `std::sys` instead of `std::env`. This also only puts it in the Unix implementation, not Windows where the functions are already threadsafe. * The `std::sys` implementation now returns `io::Result` so panics are explicitly at the `std::env` level.
2 parents 1dac3ad + 4b43e07 commit 41308a0

File tree

4 files changed

+62
-56
lines changed

4 files changed

+62
-56
lines changed

src/libstd/env.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ use ffi::{OsStr, OsString};
2323
use fmt;
2424
use io;
2525
use path::{Path, PathBuf};
26-
use sync::StaticMutex;
2726
use sys::os as os_imp;
2827

2928
/// Returns the current working directory as a `PathBuf`.
@@ -68,8 +67,6 @@ pub fn set_current_dir<P: AsRef<Path>>(p: P) -> io::Result<()> {
6867
os_imp::chdir(p.as_ref())
6968
}
7069

71-
static ENV_LOCK: StaticMutex = StaticMutex::new();
72-
7370
/// An iterator over a snapshot of the environment variables of this process.
7471
///
7572
/// This iterator is created through `std::env::vars()` and yields `(String,
@@ -133,7 +130,6 @@ pub fn vars() -> Vars {
133130
/// ```
134131
#[stable(feature = "env", since = "1.0.0")]
135132
pub fn vars_os() -> VarsOs {
136-
let _g = ENV_LOCK.lock();
137133
VarsOs { inner: os_imp::env() }
138134
}
139135

@@ -204,8 +200,9 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
204200
}
205201

206202
fn _var_os(key: &OsStr) -> Option<OsString> {
207-
let _g = ENV_LOCK.lock();
208-
os_imp::getenv(key)
203+
os_imp::getenv(key).unwrap_or_else(|e| {
204+
panic!("failed to get environment variable `{:?}`: {}", key, e)
205+
})
209206
}
210207

211208
/// Possible errors from the `env::var` method.
@@ -281,8 +278,10 @@ pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(k: K, v: V) {
281278
}
282279

283280
fn _set_var(k: &OsStr, v: &OsStr) {
284-
let _g = ENV_LOCK.lock();
285-
os_imp::setenv(k, v)
281+
os_imp::setenv(k, v).unwrap_or_else(|e| {
282+
panic!("failed to set environment variable `{:?}` to `{:?}`: {}",
283+
k, v, e)
284+
})
286285
}
287286

288287
/// Removes an environment variable from the environment of the currently running process.
@@ -322,8 +321,9 @@ pub fn remove_var<K: AsRef<OsStr>>(k: K) {
322321
}
323322

324323
fn _remove_var(k: &OsStr) {
325-
let _g = ENV_LOCK.lock();
326-
os_imp::unsetenv(k)
324+
os_imp::unsetenv(k).unwrap_or_else(|e| {
325+
panic!("failed to remove environment variable `{:?}`: {}", k, e)
326+
})
327327
}
328328

329329
/// An iterator over `Path` instances for parsing an environment variable

src/libstd/sys/unix/os.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ use path::{self, PathBuf};
2626
use ptr;
2727
use slice;
2828
use str;
29+
use sync::StaticMutex;
2930
use sys::c;
3031
use sys::fd;
32+
use sys::cvt;
3133
use vec;
3234

3335
const TMPBUF_SZ: usize = 128;
36+
static ENV_LOCK: StaticMutex = StaticMutex::new();
3437

3538
/// Returns the platform-specific value of errno
3639
pub fn errno() -> i32 {
@@ -378,6 +381,7 @@ pub unsafe fn environ() -> *mut *const *const c_char {
378381
/// Returns a vector of (variable, value) byte-vector pairs for all the
379382
/// environment variables of the current process.
380383
pub fn env() -> Env {
384+
let _g = ENV_LOCK.lock();
381385
return unsafe {
382386
let mut environ = *environ();
383387
if environ as usize == 0 {
@@ -401,35 +405,36 @@ pub fn env() -> Env {
401405
}
402406
}
403407

404-
pub fn getenv(k: &OsStr) -> Option<OsString> {
405-
unsafe {
406-
let s = k.to_cstring().unwrap();
407-
let s = libc::getenv(s.as_ptr()) as *const _;
408+
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
409+
// environment variables with a nul byte can't be set, so their value is
410+
// always None as well
411+
let k = try!(CString::new(k.as_bytes()));
412+
let _g = ENV_LOCK.lock();
413+
Ok(unsafe {
414+
let s = libc::getenv(k.as_ptr()) as *const _;
408415
if s.is_null() {
409416
None
410417
} else {
411418
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
412419
}
413-
}
420+
})
414421
}
415422

416-
pub fn setenv(k: &OsStr, v: &OsStr) {
417-
unsafe {
418-
let k = k.to_cstring().unwrap();
419-
let v = v.to_cstring().unwrap();
420-
if libc::funcs::posix01::unistd::setenv(k.as_ptr(), v.as_ptr(), 1) != 0 {
421-
panic!("failed setenv: {}", io::Error::last_os_error());
422-
}
423-
}
423+
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
424+
let k = try!(CString::new(k.as_bytes()));
425+
let v = try!(CString::new(v.as_bytes()));
426+
let _g = ENV_LOCK.lock();
427+
cvt(unsafe {
428+
libc::funcs::posix01::unistd::setenv(k.as_ptr(), v.as_ptr(), 1)
429+
}).map(|_| ())
424430
}
425431

426-
pub fn unsetenv(n: &OsStr) {
427-
unsafe {
428-
let nbuf = n.to_cstring().unwrap();
429-
if libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr()) != 0 {
430-
panic!("failed unsetenv: {}", io::Error::last_os_error());
431-
}
432-
}
432+
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
433+
let nbuf = try!(CString::new(n.as_bytes()));
434+
let _g = ENV_LOCK.lock();
435+
cvt(unsafe {
436+
libc::funcs::posix01::unistd::unsetenv(nbuf.as_ptr())
437+
}).map(|_| ())
433438
}
434439

435440
pub fn page_size() -> usize {
@@ -439,7 +444,7 @@ pub fn page_size() -> usize {
439444
}
440445

441446
pub fn temp_dir() -> PathBuf {
442-
getenv("TMPDIR".as_ref()).map(PathBuf::from).unwrap_or_else(|| {
447+
::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| {
443448
if cfg!(target_os = "android") {
444449
PathBuf::from("/data/local/tmp")
445450
} else {
@@ -449,7 +454,7 @@ pub fn temp_dir() -> PathBuf {
449454
}
450455

451456
pub fn home_dir() -> Option<PathBuf> {
452-
return getenv("HOME".as_ref()).or_else(|| unsafe {
457+
return ::env::var_os("HOME").or_else(|| unsafe {
453458
fallback()
454459
}).map(PathBuf::from);
455460

src/libstd/sys/windows/c.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub const WSASYS_STATUS_LEN: usize = 128;
3232
pub const FIONBIO: libc::c_long = 0x8004667e;
3333
pub const FD_SETSIZE: usize = 64;
3434
pub const MSG_DONTWAIT: libc::c_int = 0;
35+
pub const ERROR_ENVVAR_NOT_FOUND: libc::c_int = 203;
3536
pub const ERROR_ILLEGAL_CHARACTER: libc::c_int = 582;
3637
pub const ENABLE_ECHO_INPUT: libc::DWORD = 0x4;
3738
pub const ENABLE_EXTENDED_FLAGS: libc::DWORD = 0x80;

src/libstd/sys/windows/os.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use os::windows::ffi::EncodeWide;
2626
use path::{self, PathBuf};
2727
use ptr;
2828
use slice;
29-
use sys::c;
29+
use sys::{c, cvt};
3030
use sys::handle::Handle;
3131

3232
use libc::funcs::extra::kernel32::{
@@ -248,41 +248,41 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
248248
let mut p = p.encode_wide().collect::<Vec<_>>();
249249
p.push(0);
250250

251-
unsafe {
252-
match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) {
253-
true => Ok(()),
254-
false => Err(io::Error::last_os_error()),
255-
}
256-
}
251+
cvt(unsafe {
252+
libc::SetCurrentDirectoryW(p.as_ptr())
253+
}).map(|_| ())
257254
}
258255

259-
pub fn getenv(k: &OsStr) -> Option<OsString> {
256+
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
260257
let k = super::to_utf16_os(k);
261-
super::fill_utf16_buf(|buf, sz| unsafe {
258+
let res = super::fill_utf16_buf(|buf, sz| unsafe {
262259
libc::GetEnvironmentVariableW(k.as_ptr(), buf, sz)
263260
}, |buf| {
264261
OsStringExt::from_wide(buf)
265-
}).ok()
262+
});
263+
match res {
264+
Ok(value) => Ok(Some(value)),
265+
Err(ref e) if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND) => {
266+
Ok(None)
267+
}
268+
Err(e) => Err(e)
269+
}
266270
}
267271

268-
pub fn setenv(k: &OsStr, v: &OsStr) {
272+
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
269273
let k = super::to_utf16_os(k);
270274
let v = super::to_utf16_os(v);
271275

272-
unsafe {
273-
if libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) == 0 {
274-
panic!("failed to set env: {}", io::Error::last_os_error());
275-
}
276-
}
276+
cvt(unsafe {
277+
libc::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())
278+
}).map(|_| ())
277279
}
278280

279-
pub fn unsetenv(n: &OsStr) {
281+
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
280282
let v = super::to_utf16_os(n);
281-
unsafe {
282-
if libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) == 0 {
283-
panic!("failed to unset env: {}", io::Error::last_os_error());
284-
}
285-
}
283+
cvt(unsafe {
284+
libc::SetEnvironmentVariableW(v.as_ptr(), ptr::null())
285+
}).map(|_| ())
286286
}
287287

288288
pub struct Args {
@@ -339,8 +339,8 @@ pub fn temp_dir() -> PathBuf {
339339
}
340340

341341
pub fn home_dir() -> Option<PathBuf> {
342-
getenv("HOME".as_ref()).or_else(|| {
343-
getenv("USERPROFILE".as_ref())
342+
::env::var_os("HOME").or_else(|| {
343+
::env::var_os("USERPROFILE")
344344
}).map(PathBuf::from).or_else(|| unsafe {
345345
let me = c::GetCurrentProcess();
346346
let mut token = ptr::null_mut();

0 commit comments

Comments
 (0)