Skip to content

Commit ba98164

Browse files
committed
Auto merge of rust-lang#124636 - tbu-:pr_env_unsafe, r=petrochenkov
Make `std::env::{set_var, remove_var}` unsafe in edition 2024 Allow calling these functions without `unsafe` blocks in editions up until 2021, but don't trigger the `unused_unsafe` lint for `unsafe` blocks containing these functions. Fixes rust-lang#27970. Fixes rust-lang#90308. CC rust-lang#124866.
2 parents bbaaa79 + 4feb881 commit ba98164

File tree

12 files changed

+95
-69
lines changed

12 files changed

+95
-69
lines changed

std/src/env.rs

+66-36
Original file line numberDiff line numberDiff line change
@@ -318,22 +318,25 @@ impl Error for VarError {
318318
///
319319
/// # Safety
320320
///
321-
/// Even though this function is currently not marked as `unsafe`, it needs to
322-
/// be because invoking it can cause undefined behaviour. The function will be
323-
/// marked `unsafe` in a future version of Rust. This is tracked in
324-
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
325-
///
326321
/// This function is safe to call in a single-threaded program.
327322
///
328-
/// In multi-threaded programs, you must ensure that are no other threads
329-
/// concurrently writing or *reading*(!) from the environment through functions
330-
/// other than the ones in this module. You are responsible for figuring out
331-
/// how to achieve this, but we strongly suggest not using `set_var` or
332-
/// `remove_var` in multi-threaded programs at all.
333-
///
334-
/// Most C libraries, including libc itself do not advertise which functions
335-
/// read from the environment. Even functions from the Rust standard library do
336-
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
323+
/// This function is also always safe to call on Windows, in single-threaded
324+
/// and multi-threaded programs.
325+
///
326+
/// In multi-threaded programs on other operating systems, we strongly suggest
327+
/// not using `set_var` or `remove_var` at all. The exact requirement is: you
328+
/// must ensure that there are no other threads concurrently writing or
329+
/// *reading*(!) the environment through functions or global variables other
330+
/// than the ones in this module. The problem is that these operating systems
331+
/// do not provide a thread-safe way to read the environment, and most C
332+
/// libraries, including libc itself, do not advertise which functions read
333+
/// from the environment. Even functions from the Rust standard library may
334+
/// read the environment without going through this module, e.g. for DNS
335+
/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about
336+
/// which functions may read from the environment in future versions of a
337+
/// library. All this makes it not practically possible for you to guarantee
338+
/// that no other thread will read the environment, so the only safe option is
339+
/// to not use `set_var` or `remove_var` in multi-threaded programs at all.
337340
///
338341
/// Discussion of this unsafety on Unix may be found in:
339342
///
@@ -353,15 +356,26 @@ impl Error for VarError {
353356
/// use std::env;
354357
///
355358
/// let key = "KEY";
356-
/// env::set_var(key, "VALUE");
359+
/// unsafe {
360+
/// env::set_var(key, "VALUE");
361+
/// }
357362
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
358363
/// ```
364+
#[cfg(not(bootstrap))]
365+
#[rustc_deprecated_safe_2024]
359366
#[stable(feature = "env", since = "1.0.0")]
360-
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
367+
pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
361368
_set_var(key.as_ref(), value.as_ref())
362369
}
363370

364-
fn _set_var(key: &OsStr, value: &OsStr) {
371+
#[cfg(bootstrap)]
372+
#[allow(missing_docs)]
373+
#[stable(feature = "env", since = "1.0.0")]
374+
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
375+
unsafe { _set_var(key.as_ref(), value.as_ref()) }
376+
}
377+
378+
unsafe fn _set_var(key: &OsStr, value: &OsStr) {
365379
os_imp::setenv(key, value).unwrap_or_else(|e| {
366380
panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}")
367381
})
@@ -371,22 +385,25 @@ fn _set_var(key: &OsStr, value: &OsStr) {
371385
///
372386
/// # Safety
373387
///
374-
/// Even though this function is currently not marked as `unsafe`, it needs to
375-
/// be because invoking it can cause undefined behaviour. The function will be
376-
/// marked `unsafe` in a future version of Rust. This is tracked in
377-
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
378-
///
379388
/// This function is safe to call in a single-threaded program.
380389
///
381-
/// In multi-threaded programs, you must ensure that are no other threads
382-
/// concurrently writing or *reading*(!) from the environment through functions
383-
/// other than the ones in this module. You are responsible for figuring out
384-
/// how to achieve this, but we strongly suggest not using `set_var` or
385-
/// `remove_var` in multi-threaded programs at all.
386-
///
387-
/// Most C libraries, including libc itself do not advertise which functions
388-
/// read from the environment. Even functions from the Rust standard library do
389-
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
390+
/// This function is also always safe to call on Windows, in single-threaded
391+
/// and multi-threaded programs.
392+
///
393+
/// In multi-threaded programs on other operating systems, we strongly suggest
394+
/// not using `set_var` or `remove_var` at all. The exact requirement is: you
395+
/// must ensure that there are no other threads concurrently writing or
396+
/// *reading*(!) the environment through functions or global variables other
397+
/// than the ones in this module. The problem is that these operating systems
398+
/// do not provide a thread-safe way to read the environment, and most C
399+
/// libraries, including libc itself, do not advertise which functions read
400+
/// from the environment. Even functions from the Rust standard library may
401+
/// read the environment without going through this module, e.g. for DNS
402+
/// lookups from [`std::net::ToSocketAddrs`]. No stable guarantee is made about
403+
/// which functions may read from the environment in future versions of a
404+
/// library. All this makes it not practically possible for you to guarantee
405+
/// that no other thread will read the environment, so the only safe option is
406+
/// to not use `set_var` or `remove_var` in multi-threaded programs at all.
390407
///
391408
/// Discussion of this unsafety on Unix may be found in:
392409
///
@@ -403,22 +420,35 @@ fn _set_var(key: &OsStr, value: &OsStr) {
403420
///
404421
/// # Examples
405422
///
406-
/// ```
423+
/// ```no_run
407424
/// use std::env;
408425
///
409426
/// let key = "KEY";
410-
/// env::set_var(key, "VALUE");
427+
/// unsafe {
428+
/// env::set_var(key, "VALUE");
429+
/// }
411430
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
412431
///
413-
/// env::remove_var(key);
432+
/// unsafe {
433+
/// env::remove_var(key);
434+
/// }
414435
/// assert!(env::var(key).is_err());
415436
/// ```
437+
#[cfg(not(bootstrap))]
438+
#[rustc_deprecated_safe_2024]
416439
#[stable(feature = "env", since = "1.0.0")]
417-
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
440+
pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) {
418441
_remove_var(key.as_ref())
419442
}
420443

421-
fn _remove_var(key: &OsStr) {
444+
#[cfg(bootstrap)]
445+
#[allow(missing_docs)]
446+
#[stable(feature = "env", since = "1.0.0")]
447+
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
448+
unsafe { _remove_var(key.as_ref()) }
449+
}
450+
451+
unsafe fn _remove_var(key: &OsStr) {
422452
os_imp::unsetenv(key)
423453
.unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}"))
424454
}

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

+5-9
Original file line numberDiff line numberDiff line change
@@ -172,18 +172,14 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
172172
unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
173173
}
174174

175-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
176-
unsafe {
177-
let (k, v) = (k.to_owned(), v.to_owned());
178-
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
179-
}
175+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
176+
let (k, v) = (k.to_owned(), v.to_owned());
177+
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
180178
Ok(())
181179
}
182180

183-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
184-
unsafe {
185-
ENV.as_ref().unwrap().lock().unwrap().remove(k);
186-
}
181+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
182+
ENV.as_ref().unwrap().lock().unwrap().remove(k);
187183
Ok(())
188184
}
189185

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,13 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
157157
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
158158
}
159159

160-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
160+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
161161
let (k, v) = (k.to_owned(), v.to_owned());
162162
create_env_store().lock().unwrap().insert(k, v);
163163
Ok(())
164164
}
165165

166-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
166+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
167167
if let Some(env) = get_env_store() {
168168
env.lock().unwrap().remove(k);
169169
}

std/src/sys/pal/solid/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
191191
.flatten()
192192
}
193193

194-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
194+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
195195
run_with_cstr(k.as_bytes(), &|k| {
196196
run_with_cstr(v.as_bytes(), &|v| {
197197
let _guard = ENV_LOCK.write();
@@ -200,7 +200,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
200200
})
201201
}
202202

203-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
203+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
204204
run_with_cstr(n.as_bytes(), &|nbuf| {
205205
let _guard = ENV_LOCK.write();
206206
cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)

std/src/sys/pal/teeos/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
109109
None
110110
}
111111

112-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
112+
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
113113
Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
114114
}
115115

116-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
116+
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
117117
Err(io::Error::new(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
118118
}
119119

std/src/sys/pal/uefi/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -203,11 +203,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
203203
None
204204
}
205205

206-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
206+
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
207207
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
208208
}
209209

210-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
210+
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
211211
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
212212
}
213213

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -675,19 +675,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
675675
.flatten()
676676
}
677677

678-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
678+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
679679
run_with_cstr(k.as_bytes(), &|k| {
680680
run_with_cstr(v.as_bytes(), &|v| {
681681
let _guard = ENV_LOCK.write();
682-
cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
682+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
683683
})
684684
})
685685
}
686686

687-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
687+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
688688
run_with_cstr(n.as_bytes(), &|nbuf| {
689689
let _guard = ENV_LOCK.write();
690-
cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
690+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
691691
})
692692
}
693693

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
9696
None
9797
}
9898

99-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
99+
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
100100
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
101101
}
102102

103-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
103+
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
104104
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
105105
}
106106

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
244244
.flatten()
245245
}
246246

247-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
247+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
248248
run_with_cstr(k.as_bytes(), &|k| {
249249
run_with_cstr(v.as_bytes(), &|v| unsafe {
250250
let _guard = env_write_lock();
@@ -253,7 +253,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
253253
})
254254
}
255255

256-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
256+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
257257
run_with_cstr(n.as_bytes(), &|nbuf| unsafe {
258258
let _guard = env_write_lock();
259259
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,16 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
302302
.ok()
303303
}
304304

305-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
305+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
306306
let k = to_u16s(k)?;
307307
let v = to_u16s(v)?;
308308

309-
cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
309+
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
310310
}
311311

312-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
312+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
313313
let v = to_u16s(n)?;
314-
cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
314+
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
315315
}
316316

317317
pub fn temp_dir() -> PathBuf {

std/src/sys/pal/xous/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
149149
None
150150
}
151151

152-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
152+
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
153153
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
154154
}
155155

156-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
156+
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
157157
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
158158
}
159159

std/src/sys/pal/zkvm/os.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ pub fn getenv(varname: &OsStr) -> Option<OsString> {
115115
Some(OsString::from_inner(os_str::Buf { inner: u8s.to_vec() }))
116116
}
117117

118-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
118+
pub unsafe fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
119119
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
120120
}
121121

122-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
122+
pub unsafe fn unsetenv(_: &OsStr) -> io::Result<()> {
123123
Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
124124
}
125125

0 commit comments

Comments
 (0)