Skip to content

Commit f7e8269

Browse files
committed
Auto merge of rust-lang#124636 - tbu-:pr_env_unsafe, r=<try>
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.
2 parents 9fa862f + 0a93cf6 commit f7e8269

File tree

23 files changed

+296
-74
lines changed

23 files changed

+296
-74
lines changed

compiler/rustc_driver_impl/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,11 @@ fn ice_path() -> &'static Option<PathBuf> {
13071307
/// internal features.
13081308
///
13091309
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
1310-
pub fn install_ice_hook(
1310+
///
1311+
/// # Unsafety
1312+
///
1313+
/// This function modifies the environment and has the same safety as [`std::env::set_var`].
1314+
pub unsafe fn install_ice_hook(
13111315
bug_report_url: &'static str,
13121316
extra_info: fn(&DiagCtxt),
13131317
) -> Arc<AtomicBool> {
@@ -1520,7 +1524,8 @@ pub fn main() -> ! {
15201524
init_rustc_env_logger(&early_dcx);
15211525
signal_handler::install();
15221526
let mut callbacks = TimePassesCallbacks::default();
1523-
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
1527+
// We're single-threaded at this point, so it's okay to call `install_ice_hook`.
1528+
let using_internal_features = unsafe { install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()) };
15241529
install_ctrlc_handler();
15251530

15261531
let exit_code = catch_with_exit_code(|| {

compiler/rustc_feature/src/builtin_attrs.rs

+6
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
584584
"rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
585585
through unstable paths"
586586
),
587+
rustc_attr!(
588+
rustc_deprecated_safe_2024, Normal, template!(Word), WarnFollowing,
589+
EncodeCrossCrate::Yes,
590+
"rustc_deprecated_safe_2024 is supposed to be used in libstd only",
591+
),
592+
587593

588594
// ==========================================================================
589595
// Internal attributes: Type system related:

compiler/rustc_mir_build/src/check_unsafety.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,19 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
109109
);
110110
self.suggest_unsafe_block = false;
111111
}
112-
SafetyContext::Safe => {
113-
kind.emit_requires_unsafe_err(
112+
SafetyContext::Safe => match kind {
113+
// Allow calls to deprecated-safe unsafe functions if the
114+
// caller is from an edition before 2024.
115+
UnsafeOpKind::CallToUnsafeFunction(Some(id))
116+
if !span.at_least_rust_2024()
117+
&& self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) => {}
118+
_ => kind.emit_requires_unsafe_err(
114119
self.tcx,
115120
span,
116121
self.hir_context,
117122
unsafe_op_in_unsafe_fn_allowed,
118-
);
119-
}
123+
),
124+
},
120125
}
121126
}
122127

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,7 @@ symbols! {
15691569
rustc_def_path,
15701570
rustc_default_body_unstable,
15711571
rustc_deny_explicit_impl,
1572+
rustc_deprecated_safe_2024,
15721573
rustc_diagnostic_item,
15731574
rustc_diagnostic_macros,
15741575
rustc_dirty,

library/std/src/env.rs

+117-16
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,52 @@ 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).
321+
/// This function is safe to call in a single-threaded program.
322+
///
323+
/// In multi-threaded programs, you must ensure that are no other threads
324+
/// concurrently writing or *reading*(!) from the environment through functions
325+
/// other than the ones in this module. You are responsible for figuring out
326+
/// how to achieve this, but we strongly suggest not using `set_var` or
327+
/// `remove_var` in multi-threaded programs at all.
328+
///
329+
/// Most C libraries, including libc itself do not advertise which functions
330+
/// read from the environment. Even functions from the Rust standard library do
331+
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
332+
///
333+
/// Discussion of this unsafety on Unix may be found in:
334+
///
335+
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
336+
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
337+
///
338+
/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
339+
///
340+
/// # Panics
341+
///
342+
/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
343+
/// or the NUL character `'\0'`, or when `value` contains the NUL character.
344+
///
345+
/// # Examples
346+
///
347+
/// ```
348+
/// use std::env;
349+
///
350+
/// let key = "KEY";
351+
/// unsafe {
352+
/// env::set_var(key, "VALUE");
353+
/// }
354+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
355+
/// ```
356+
#[cfg(not(bootstrap))]
357+
#[rustc_deprecated_safe_2024]
358+
#[stable(feature = "env", since = "1.0.0")]
359+
pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
360+
_set_var(key.as_ref(), value.as_ref())
361+
}
362+
363+
/// Sets the environment variable `key` to the value `value` for the currently running
364+
/// process.
365+
///
366+
/// # Safety
325367
///
326368
/// This function is safe to call in a single-threaded program.
327369
///
@@ -350,18 +392,23 @@ impl Error for VarError {
350392
/// # Examples
351393
///
352394
/// ```
395+
/// # #![allow(unused_unsafe)]
396+
///
353397
/// use std::env;
354398
///
355399
/// let key = "KEY";
356-
/// env::set_var(key, "VALUE");
400+
/// unsafe {
401+
/// env::set_var(key, "VALUE");
402+
/// }
357403
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
358404
/// ```
405+
#[cfg(bootstrap)]
359406
#[stable(feature = "env", since = "1.0.0")]
360407
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
361-
_set_var(key.as_ref(), value.as_ref())
408+
unsafe { _set_var(key.as_ref(), value.as_ref()) }
362409
}
363410

364-
fn _set_var(key: &OsStr, value: &OsStr) {
411+
unsafe fn _set_var(key: &OsStr, value: &OsStr) {
365412
os_imp::setenv(key, value).unwrap_or_else(|e| {
366413
panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}")
367414
})
@@ -371,11 +418,6 @@ fn _set_var(key: &OsStr, value: &OsStr) {
371418
///
372419
/// # Safety
373420
///
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-
///
379421
/// This function is safe to call in a single-threaded program.
380422
///
381423
/// In multi-threaded programs, you must ensure that are no other threads
@@ -403,22 +445,81 @@ fn _set_var(key: &OsStr, value: &OsStr) {
403445
///
404446
/// # Examples
405447
///
448+
/// ```no_run
449+
/// use std::env;
450+
///
451+
/// let key = "KEY";
452+
/// unsafe {
453+
/// env::set_var(key, "VALUE");
454+
/// }
455+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
456+
///
457+
/// unsafe {
458+
/// env::remove_var(key);
459+
/// }
460+
/// assert!(env::var(key).is_err());
406461
/// ```
462+
#[cfg(not(bootstrap))]
463+
#[rustc_deprecated_safe_2024]
464+
#[stable(feature = "env", since = "1.0.0")]
465+
pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) {
466+
_remove_var(key.as_ref())
467+
}
468+
469+
/// Removes an environment variable from the environment of the currently running process.
470+
///
471+
/// # Safety
472+
///
473+
/// This function is safe to call in a single-threaded program.
474+
///
475+
/// In multi-threaded programs, you must ensure that are no other threads
476+
/// concurrently writing or *reading*(!) from the environment through functions
477+
/// other than the ones in this module. You are responsible for figuring out
478+
/// how to achieve this, but we strongly suggest not using `set_var` or
479+
/// `remove_var` in multi-threaded programs at all.
480+
///
481+
/// Most C libraries, including libc itself do not advertise which functions
482+
/// read from the environment. Even functions from the Rust standard library do
483+
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
484+
///
485+
/// Discussion of this unsafety on Unix may be found in:
486+
///
487+
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
488+
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
489+
///
490+
/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
491+
///
492+
/// # Panics
493+
///
494+
/// This function may panic if `key` is empty, contains an ASCII equals sign
495+
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
496+
/// character.
497+
///
498+
/// # Examples
499+
///
500+
/// ```no_run
501+
/// # #![allow(unused_unsafe)]
502+
///
407503
/// use std::env;
408504
///
409505
/// let key = "KEY";
410-
/// env::set_var(key, "VALUE");
506+
/// unsafe {
507+
/// env::set_var(key, "VALUE");
508+
/// }
411509
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
412510
///
413-
/// env::remove_var(key);
511+
/// unsafe {
512+
/// env::remove_var(key);
513+
/// }
414514
/// assert!(env::var(key).is_err());
415515
/// ```
516+
#[cfg(bootstrap)]
416517
#[stable(feature = "env", since = "1.0.0")]
417518
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
418-
_remove_var(key.as_ref())
519+
unsafe { _remove_var(key.as_ref()) }
419520
}
420521

421-
fn _remove_var(key: &OsStr) {
522+
unsafe fn _remove_var(key: &OsStr) {
422523
os_imp::unsetenv(key)
423524
.unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}"))
424525
}

library/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

library/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
}

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -191,19 +191,19 @@ 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();
198-
cvt_env(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
198+
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
199199
})
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();
206-
cvt_env(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
206+
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
207207
})
208208
}
209209

library/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

library/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

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -651,19 +651,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
651651
.flatten()
652652
}
653653

654-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
654+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
655655
run_with_cstr(k.as_bytes(), &|k| {
656656
run_with_cstr(v.as_bytes(), &|v| {
657657
let _guard = ENV_LOCK.write();
658-
cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
658+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
659659
})
660660
})
661661
}
662662

663-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
663+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
664664
run_with_cstr(n.as_bytes(), &|nbuf| {
665665
let _guard = ENV_LOCK.write();
666-
cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
666+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
667667
})
668668
}
669669

library/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

library/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)

0 commit comments

Comments
 (0)