Skip to content

Commit 016612d

Browse files
authored
Rollup merge of #86183 - inquisitivecrystal:env-nul, r=m-ou-se
Change environment variable getters to error recoverably This PR changes the standard library environment variable getter functions to error recoverably (i.e. not panic) when given an invalid value. On some platforms, it is invalid for environment variable names to contain `'\0'` or `'='`, or for their values to contain `'\0'`. Currently, the standard library panics when manipulating environment variables with names or values that violate these invariants. However, this behavior doesn't make a lot of sense, at least in the case of getters. If the environment variable is missing, the standard library just returns an error value, rather than panicking. It doesn't make sense to treat the case where the variable is invalid any differently from that. See the [internals thread](https://internals.rust-lang.org/t/why-should-std-var-panic/14847) for discussion. Thus, this PR changes the functions to error recoverably in this case as well. If desired, I could change the functions that manipulate environment variables in other ways as well. I didn't do that here because it wasn't entirely clear what to change them to. Should they error silently or do something else? If someone tells me how to change them, I'm happy to implement the changes. This fixes #86082, an ICE that arises from the current behavior. It also adds a regression test to make sure the ICE does not occur again in the future. `@rustbot` label +T-libs r? `@joshtriplett`
2 parents cd5a90f + d9752c7 commit 016612d

File tree

8 files changed

+38
-52
lines changed

8 files changed

+38
-52
lines changed

library/std/src/env.rs

+9-17
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,9 @@ impl fmt::Debug for VarsOs {
185185
///
186186
/// # Errors
187187
///
188-
/// Errors if the environment variable is not present.
189-
/// Errors if the environment variable is not valid Unicode. If this is not desired, consider using
190-
/// [`var_os`].
191-
///
192-
/// # Panics
193-
///
194-
/// This function may panic if `key` is empty, contains an ASCII equals sign
195-
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
196-
/// character.
188+
/// Returns `[None]` if the environment variable isn't set.
189+
/// Returns `[None]` if the environment variable is not valid Unicode. If this is not
190+
/// desired, consider using [`var_os`].
197191
///
198192
/// # Examples
199193
///
@@ -219,18 +213,17 @@ fn _var(key: &OsStr) -> Result<String, VarError> {
219213
}
220214

221215
/// Fetches the environment variable `key` from the current process, returning
222-
/// [`None`] if the variable isn't set.
223-
///
224-
/// # Panics
225-
///
226-
/// This function may panic if `key` is empty, contains an ASCII equals sign
227-
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
228-
/// character.
216+
/// [`None`] if the variable isn't set or there's another error.
229217
///
230218
/// Note that the method will not check if the environment variable
231219
/// is valid Unicode. If you want to have an error on invalid UTF-8,
232220
/// use the [`var`] function instead.
233221
///
222+
/// # Errors
223+
///
224+
/// Returns `[None]` if the variable isn't set.
225+
/// May return `[None]` if the variable value contains the NUL character.
226+
///
234227
/// # Examples
235228
///
236229
/// ```
@@ -249,7 +242,6 @@ pub fn var_os<K: AsRef<OsStr>>(key: K) -> Option<OsString> {
249242

250243
fn _var_os(key: &OsStr) -> Option<OsString> {
251244
os_imp::getenv(key)
252-
.unwrap_or_else(|e| panic!("failed to get environment variable `{:?}`: {}", key, e))
253245
}
254246

255247
/// The error type for operations interacting with environment variables.

library/std/src/sys/hermit/os.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,8 @@ pub fn env() -> Env {
140140
}
141141
}
142142

143-
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
144-
unsafe {
145-
match ENV.as_ref().unwrap().lock().unwrap().get_mut(k) {
146-
Some(value) => Ok(Some(value.clone())),
147-
None => Ok(None),
148-
}
149-
}
143+
pub fn getenv(k: &OsStr) -> Option<OsString> {
144+
unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
150145
}
151146

152147
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {

library/std/src/sys/sgx/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ pub fn env() -> Env {
106106
get_env_store().map(|env| clone_to_vec(&env.lock().unwrap())).unwrap_or_default().into_iter()
107107
}
108108

109-
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
110-
Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned()))
109+
pub fn getenv(k: &OsStr) -> Option<OsString> {
110+
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
111111
}
112112

113113
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {

library/std/src/sys/unix/os.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -532,19 +532,18 @@ pub fn env() -> Env {
532532
}
533533
}
534534

535-
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
535+
pub fn getenv(k: &OsStr) -> Option<OsString> {
536536
// environment variables with a nul byte can't be set, so their value is
537537
// always None as well
538-
let k = CString::new(k.as_bytes())?;
538+
let k = CString::new(k.as_bytes()).ok()?;
539539
unsafe {
540540
let _guard = env_read_lock();
541541
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
542-
let ret = if s.is_null() {
542+
if s.is_null() {
543543
None
544544
} else {
545545
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
546-
};
547-
Ok(ret)
546+
}
548547
}
549548
}
550549

library/std/src/sys/unsupported/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ pub fn env() -> Env {
7676
panic!("not supported on this platform")
7777
}
7878

79-
pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
80-
Ok(None)
79+
pub fn getenv(_: &OsStr) -> Option<OsString> {
80+
None
8181
}
8282

8383
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {

library/std/src/sys/wasi/os.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,16 @@ pub fn env() -> Env {
175175
}
176176
}
177177

178-
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
179-
let k = CString::new(k.as_bytes())?;
178+
pub fn getenv(k: &OsStr) -> Option<OsString> {
179+
let k = CString::new(k.as_bytes()).ok()?;
180180
unsafe {
181181
let _guard = env_lock();
182182
let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
183-
let ret = if s.is_null() {
183+
if s.is_null() {
184184
None
185185
} else {
186186
Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
187-
};
188-
Ok(ret)
187+
}
189188
}
190189
}
191190

library/std/src/sys/windows/os.rs

+5-14
Original file line numberDiff line numberDiff line change
@@ -253,22 +253,13 @@ pub fn chdir(p: &path::Path) -> io::Result<()> {
253253
cvt(unsafe { c::SetCurrentDirectoryW(p.as_ptr()) }).map(drop)
254254
}
255255

256-
pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> {
257-
let k = to_u16s(k)?;
258-
let res = super::fill_utf16_buf(
256+
pub fn getenv(k: &OsStr) -> Option<OsString> {
257+
let k = to_u16s(k).ok()?;
258+
super::fill_utf16_buf(
259259
|buf, sz| unsafe { c::GetEnvironmentVariableW(k.as_ptr(), buf, sz) },
260260
|buf| OsStringExt::from_wide(buf),
261-
);
262-
match res {
263-
Ok(value) => Ok(Some(value)),
264-
Err(e) => {
265-
if e.raw_os_error() == Some(c::ERROR_ENVVAR_NOT_FOUND as i32) {
266-
Ok(None)
267-
} else {
268-
Err(e)
269-
}
270-
}
271-
}
261+
)
262+
.ok()
272263
}
273264

274265
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// check-pass
2+
//
3+
// Regression test for issue #86082
4+
//
5+
// Checks that option_env! does not panic on receiving an invalid
6+
// environment variable name.
7+
8+
fn main() {
9+
option_env!("\0=");
10+
}

0 commit comments

Comments
 (0)