diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 5ed9fa9d6f0fd..02e7664858e5d 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -338,10 +338,43 @@ impl Error for VarError { /// ``` #[stable(feature = "env", since = "1.0.0")] pub fn set_var, V: AsRef>(key: K, value: V) { + // Safety: This isn't sound. See #27970 for details. + unsafe { _set_var(key.as_ref(), value.as_ref()) } +} + +/// Sets the environment variable `key` to the value `value` for the currently running +/// process. +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign `'='` +/// or the NUL character `'\0'`, or when `value` contains the NUL character. +/// +/// # Safety +/// +/// Some platforms only expose non-threadsafe APIs for altering the environment. +/// On these platforms, you must ensure this method is not called when the +/// program is multithreaded and any other thread could be reading or writing +/// the environment. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(unsafe_env)] +/// use std::env; +/// +/// let key = "KEY"; +/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded! +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// ``` +#[unstable(feature = "unsafe_env", issue = "none")] +pub unsafe fn set, V: AsRef>(key: K, value: V) { _set_var(key.as_ref(), value.as_ref()) } -fn _set_var(key: &OsStr, value: &OsStr) { +// Safety: This can only be called when the program is single-threaded or when +// no other thread touches the environment. +unsafe fn _set_var(key: &OsStr, value: &OsStr) { os_imp::setenv(key, value).unwrap_or_else(|e| { panic!("failed to set environment variable `{:?}` to `{:?}`: {}", key, value, e) }) @@ -380,10 +413,46 @@ fn _set_var(key: &OsStr, value: &OsStr) { /// ``` #[stable(feature = "env", since = "1.0.0")] pub fn remove_var>(key: K) { + // Safety: This isn't sound. See #27970 for details. + unsafe { _remove_var(key.as_ref()) } +} + +/// Removes an environment variable from the environment of the currently running process. +/// +/// # Panics +/// +/// This function may panic if `key` is empty, contains an ASCII equals sign +/// `'='` or the NUL character `'\0'`, or when the value contains the NUL +/// character. +/// +/// # Safety +/// +/// Some platforms only expose non-threadsafe APIs for altering the environment. +/// On these platforms, you must ensure this method is not called when the +/// program is multithreaded and any other thread could be reading or writing +/// the environment. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(unsafe_env)] +/// use std::env; +/// +/// let key = "KEY"; +/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded! +/// assert_eq!(env::var(key), Ok("VALUE".to_string())); +/// +/// unsafe { env::unset(key); } // Make sure you're single-threaded! +/// assert!(env::var(key).is_err()); +/// ``` +#[unstable(feature = "unsafe_env", issue = "none")] +pub unsafe fn unset>(key: K) { _remove_var(key.as_ref()) } -fn _remove_var(key: &OsStr) { +// Safety: This can only be called when the program is single-threaded or when +// no other thread touches the environment. +unsafe fn _remove_var(key: &OsStr) { os_imp::unsetenv(key) .unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", key, e)) } diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs index 8f927df85be5d..b8114f8456129 100644 --- a/library/std/src/sys/hermit/os.rs +++ b/library/std/src/sys/hermit/os.rs @@ -144,18 +144,14 @@ pub fn getenv(k: &OsStr) -> Option { unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() } } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - unsafe { - let (k, v) = (k.to_owned(), v.to_owned()); - ENV.as_ref().unwrap().lock().unwrap().insert(k, v); - } +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + ENV.as_ref().unwrap().lock().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { - unsafe { - ENV.as_ref().unwrap().lock().unwrap().remove(k); - } +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { + ENV.as_ref().unwrap().lock().unwrap().remove(k); Ok(()) } diff --git a/library/std/src/sys/sgx/os.rs b/library/std/src/sys/sgx/os.rs index 5f8b8def7c670..5ff40626288f0 100644 --- a/library/std/src/sys/sgx/os.rs +++ b/library/std/src/sys/sgx/os.rs @@ -110,13 +110,13 @@ pub fn getenv(k: &OsStr) -> Option { get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()) } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let (k, v) = (k.to_owned(), v.to_owned()); create_env_store().lock().unwrap().insert(k, v); Ok(()) } -pub fn unsetenv(k: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> { if let Some(env) = get_env_store() { env.lock().unwrap().remove(k); } diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 22239e1fa8ebc..f8295c5cb1875 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -151,23 +151,19 @@ pub fn getenv(k: &OsStr) -> Option { } } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = CString::new(k.as_bytes())?; let v = CString::new(v.as_bytes())?; - unsafe { - let _guard = ENV_LOCK.write(); - cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + let _guard = ENV_LOCK.write(); + cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; - unsafe { - let _guard = ENV_LOCK.write(); - cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + let _guard = ENV_LOCK.write(); + cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop) } /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 0b6cdb923bd6a..48efe8fdbd1a2 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -538,23 +538,21 @@ pub fn getenv(k: &OsStr) -> Option { } } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +// Safety: This can only be called when the program is single-threaded. +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = CString::new(k.as_bytes())?; let v = CString::new(v.as_bytes())?; - unsafe { - let _guard = ENV_LOCK.write(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + let _guard = ENV_LOCK.write(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +// Safety: This can only be called when the program is single-threaded. +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; - unsafe { - let _guard = ENV_LOCK.write(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + let _guard = ENV_LOCK.write(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } #[cfg(not(target_os = "espidf"))] diff --git a/library/std/src/sys/unsupported/os.rs b/library/std/src/sys/unsupported/os.rs index e150ae143ad99..b80179a22afe2 100644 --- a/library/std/src/sys/unsupported/os.rs +++ b/library/std/src/sys/unsupported/os.rs @@ -80,11 +80,11 @@ pub fn getenv(_: &OsStr) -> Option { None } -pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform")) } -pub fn unsetenv(_: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> { Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform")) } diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs index c5229a188342a..563dffa309430 100644 --- a/library/std/src/sys/wasi/os.rs +++ b/library/std/src/sys/wasi/os.rs @@ -188,23 +188,19 @@ pub fn getenv(k: &OsStr) -> Option { } } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = CString::new(k.as_bytes())?; let v = CString::new(v.as_bytes())?; - unsafe { - let _guard = env_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } + let _guard = env_lock(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; - unsafe { - let _guard = env_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } + let _guard = env_lock(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) } pub fn temp_dir() -> PathBuf { diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 5f8556c3bc376..1f07370ff517a 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -262,16 +262,16 @@ pub fn getenv(k: &OsStr) -> Option { .ok() } -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { +pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = to_u16s(k)?; let v = to_u16s(v)?; - cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop) + cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop) } -pub fn unsetenv(n: &OsStr) -> io::Result<()> { +pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { let v = to_u16s(n)?; - cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop) + cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop) } pub fn temp_dir() -> PathBuf {