-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make std::env::{set_var, remove_var}
unsafe in edition 2024
#124636
Changes from all commits
5d8f9b4
8cf4980
d7680e3
44f9f8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -318,22 +318,25 @@ impl Error for VarError { | |
/// | ||
/// # Safety | ||
/// | ||
/// Even though this function is currently not marked as `unsafe`, it needs to | ||
/// be because invoking it can cause undefined behaviour. The function will be | ||
/// marked `unsafe` in a future version of Rust. This is tracked in | ||
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). | ||
/// | ||
/// This function is safe to call in a single-threaded program. | ||
/// | ||
/// In multi-threaded programs, you must ensure that are no other threads | ||
/// concurrently writing or *reading*(!) from the environment through functions | ||
/// other than the ones in this module. You are responsible for figuring out | ||
/// how to achieve this, but we strongly suggest not using `set_var` or | ||
/// `remove_var` in multi-threaded programs at all. | ||
/// | ||
/// Most C libraries, including libc itself do not advertise which functions | ||
/// read from the environment. Even functions from the Rust standard library do | ||
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. | ||
/// This function is also always safe to call on Windows, in single-threaded | ||
/// and multi-threaded programs. | ||
/// | ||
/// In multi-threaded programs on other operating systems, we strongly suggest | ||
/// not using `set_var` or `remove_var` at all. The exact requirement is: you | ||
/// must ensure that there are no other threads concurrently writing or | ||
/// *reading*(!) the environment through functions or global variables other | ||
/// than the ones in this module. The problem is that these operating systems | ||
/// do not provide a thread-safe way to read the environment, and most C | ||
/// libraries, including libc itself, do not advertise which functions read | ||
/// from the environment. Even functions from the Rust standard library may | ||
/// read the environment without going through this module, e.g. for DNS | ||
/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about | ||
/// which functions may read from the environment in future versions of a | ||
/// library. All this makes it not practically possible for you to guarantee | ||
/// that no other thread will read the environment, so the only safe option is | ||
/// to not use `set_var` or `remove_var` in multi-threaded programs at all. | ||
petrochenkov marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+338
to
+339
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Can't we use a static Mutex to guard env reads and writes? If so, saying that "the only option is not to use this" sounds misleading. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No we cannot. There's a lot of discussion about that on the linked issues but in short that would only make it safe for people sharing the same |
||
/// | ||
/// Discussion of this unsafety on Unix may be found in: | ||
/// | ||
|
@@ -353,15 +356,26 @@ impl Error for VarError { | |
/// use std::env; | ||
/// | ||
/// let key = "KEY"; | ||
/// env::set_var(key, "VALUE"); | ||
/// unsafe { | ||
/// env::set_var(key, "VALUE"); | ||
/// } | ||
/// assert_eq!(env::var(key), Ok("VALUE".to_string())); | ||
/// ``` | ||
#[cfg(not(bootstrap))] | ||
#[rustc_deprecated_safe_2024] | ||
petrochenkov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[stable(feature = "env", since = "1.0.0")] | ||
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { | ||
pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { | ||
_set_var(key.as_ref(), value.as_ref()) | ||
} | ||
|
||
fn _set_var(key: &OsStr, value: &OsStr) { | ||
#[cfg(bootstrap)] | ||
tbu- marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[allow(missing_docs)] | ||
#[stable(feature = "env", since = "1.0.0")] | ||
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) { | ||
unsafe { _set_var(key.as_ref(), value.as_ref()) } | ||
} | ||
|
||
unsafe fn _set_var(key: &OsStr, value: &OsStr) { | ||
os_imp::setenv(key, value).unwrap_or_else(|e| { | ||
panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}") | ||
}) | ||
|
@@ -371,22 +385,25 @@ fn _set_var(key: &OsStr, value: &OsStr) { | |
/// | ||
/// # Safety | ||
/// | ||
/// Even though this function is currently not marked as `unsafe`, it needs to | ||
/// be because invoking it can cause undefined behaviour. The function will be | ||
/// marked `unsafe` in a future version of Rust. This is tracked in | ||
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970). | ||
/// | ||
/// This function is safe to call in a single-threaded program. | ||
/// | ||
/// In multi-threaded programs, you must ensure that are no other threads | ||
/// concurrently writing or *reading*(!) from the environment through functions | ||
/// other than the ones in this module. You are responsible for figuring out | ||
/// how to achieve this, but we strongly suggest not using `set_var` or | ||
/// `remove_var` in multi-threaded programs at all. | ||
/// | ||
/// Most C libraries, including libc itself do not advertise which functions | ||
/// read from the environment. Even functions from the Rust standard library do | ||
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`]. | ||
/// This function is also always safe to call on Windows, in single-threaded | ||
/// and multi-threaded programs. | ||
/// | ||
/// In multi-threaded programs on other operating systems, we strongly suggest | ||
/// not using `set_var` or `remove_var` at all. The exact requirement is: you | ||
/// must ensure that there are no other threads concurrently writing or | ||
/// *reading*(!) the environment through functions or global variables other | ||
/// than the ones in this module. The problem is that these operating systems | ||
/// do not provide a thread-safe way to read the environment, and most C | ||
/// libraries, including libc itself, do not advertise which functions read | ||
/// from the environment. Even functions from the Rust standard library may | ||
/// read the environment without going through this module, e.g. for DNS | ||
/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about | ||
/// which functions may read from the environment in future versions of a | ||
/// library. All this makes it not practically possible for you to guarantee | ||
/// that no other thread will read the environment, so the only safe option is | ||
/// to not use `set_var` or `remove_var` in multi-threaded programs at all. | ||
/// | ||
/// Discussion of this unsafety on Unix may be found in: | ||
/// | ||
|
@@ -403,22 +420,35 @@ fn _set_var(key: &OsStr, value: &OsStr) { | |
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// ```no_run | ||
/// use std::env; | ||
/// | ||
/// let key = "KEY"; | ||
/// env::set_var(key, "VALUE"); | ||
/// unsafe { | ||
/// env::set_var(key, "VALUE"); | ||
/// } | ||
/// assert_eq!(env::var(key), Ok("VALUE".to_string())); | ||
/// | ||
/// env::remove_var(key); | ||
/// unsafe { | ||
/// env::remove_var(key); | ||
/// } | ||
/// assert!(env::var(key).is_err()); | ||
/// ``` | ||
#[cfg(not(bootstrap))] | ||
#[rustc_deprecated_safe_2024] | ||
#[stable(feature = "env", since = "1.0.0")] | ||
pub fn remove_var<K: AsRef<OsStr>>(key: K) { | ||
pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) { | ||
_remove_var(key.as_ref()) | ||
} | ||
|
||
fn _remove_var(key: &OsStr) { | ||
#[cfg(bootstrap)] | ||
#[allow(missing_docs)] | ||
#[stable(feature = "env", since = "1.0.0")] | ||
pub fn remove_var<K: AsRef<OsStr>>(key: K) { | ||
unsafe { _remove_var(key.as_ref()) } | ||
} | ||
|
||
unsafe fn _remove_var(key: &OsStr) { | ||
os_imp::unsetenv(key) | ||
.unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}")) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be incorrect if we mark any other functions with this attribute where the unsafety is for a different reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it'll be fixed before #125970 is merged. This PR was intentionally minimal so it could definitely make it into the Rust 2024 edition.