Skip to content

Commit 49f8cf1

Browse files
committed
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.
1 parent 9084601 commit 49f8cf1

File tree

27 files changed

+253
-98
lines changed

27 files changed

+253
-98
lines changed

compiler/rustc_driver_impl/src/lib.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,11 @@ fn ice_path() -> &'static Option<PathBuf> {
13101310
/// internal features.
13111311
///
13121312
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
1313-
pub fn install_ice_hook(
1313+
///
1314+
/// # Unsafety
1315+
///
1316+
/// This function modifies the environment and has the same safety as [`std::env::set_var`].
1317+
pub unsafe fn install_ice_hook(
13141318
bug_report_url: &'static str,
13151319
extra_info: fn(&DiagCtxt),
13161320
) -> Arc<AtomicBool> {
@@ -1523,7 +1527,8 @@ pub fn main() -> ! {
15231527
init_rustc_env_logger(&early_dcx);
15241528
signal_handler::install();
15251529
let mut callbacks = TimePassesCallbacks::default();
1526-
let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
1530+
// We're single-threaded at this point, so it's okay to call `install_ice_hook`.
1531+
let using_internal_features = unsafe { install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()) };
15271532
install_ctrlc_handler();
15281533

15291534
let exit_code = catch_with_exit_code(|| {

compiler/rustc_hir/src/lang_items.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,9 @@ language_item_table! {
379379

380380
String, sym::String, string, Target::Struct, GenericRequirement::None;
381381
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
382+
383+
EnvSetVar, sym::env_set_var, env_set_var, Target::Fn, GenericRequirement::None;
384+
EnvRemoveVar, sym::env_remove_var, env_remove_var, Target::Fn, GenericRequirement::None;
382385
}
383386

384387
pub enum GenericRequirement {

compiler/rustc_interface/src/passes.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,16 @@ fn configure_and_expand(
178178
new_path.push(path);
179179
}
180180
}
181-
env::set_var(
182-
"PATH",
183-
&env::join_paths(
184-
new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()),
185-
)
186-
.unwrap(),
187-
);
181+
// FIXME: This looks unsafe.
182+
unsafe {
183+
env::set_var(
184+
"PATH",
185+
&env::join_paths(
186+
new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()),
187+
)
188+
.unwrap(),
189+
);
190+
}
188191
}
189192

190193
// Create the config for macro expansion
@@ -223,7 +226,10 @@ fn configure_and_expand(
223226
}
224227

225228
if cfg!(windows) {
226-
env::set_var("PATH", &old_path);
229+
unsafe {
230+
// FIXME: This looks unsafe.
231+
env::set_var("PATH", &old_path);
232+
}
227233
}
228234

229235
krate

compiler/rustc_mir_build/src/check_unsafety.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ struct UnsafetyVisitor<'a, 'tcx> {
4141
/// Flag to ensure that we only suggest wrapping the entire function body in
4242
/// an unsafe block once.
4343
suggest_unsafe_block: bool,
44+
45+
/// Functions that are only considered `unsafe` since Rust 2024.
46+
///
47+
/// - `std::env::set_var`
48+
/// - `std::env::remove_var`
49+
rust_2024_unsafe_fns: &'a [DefId],
4450
}
4551

4652
impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
@@ -109,14 +115,16 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
109115
);
110116
self.suggest_unsafe_block = false;
111117
}
112-
SafetyContext::Safe => {
113-
kind.emit_requires_unsafe_err(
118+
SafetyContext::Safe => match kind {
119+
UnsafeOpKind::CallToUnsafeFunction(Some(id))
120+
if !span.at_least_rust_2024() && self.rust_2024_unsafe_fns.contains(&id) => {}
121+
_ => kind.emit_requires_unsafe_err(
114122
self.tcx,
115123
span,
116124
self.hir_context,
117125
unsafe_op_in_unsafe_fn_allowed,
118-
);
119-
}
126+
),
127+
},
120128
}
121129
}
122130

@@ -154,6 +162,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
154162
inside_adt: false,
155163
warnings: self.warnings,
156164
suggest_unsafe_block: self.suggest_unsafe_block,
165+
rust_2024_unsafe_fns: self.rust_2024_unsafe_fns,
157166
};
158167
inner_visitor.visit_expr(&inner_thir[expr]);
159168
// Unsafe blocks can be used in the inner body, make sure to take it into account
@@ -918,6 +927,13 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
918927
return;
919928
}
920929

930+
let mut rust_2024_unsafe_fns = Vec::new();
931+
if let Some(def) = tcx.lang_items().env_set_var() {
932+
rust_2024_unsafe_fns.push(def);
933+
}
934+
if let Some(def) = tcx.lang_items().env_remove_var() {
935+
rust_2024_unsafe_fns.push(def);
936+
}
921937
let hir_id = tcx.local_def_id_to_hir_id(def);
922938
let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
923939
if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
@@ -940,6 +956,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
940956
inside_adt: false,
941957
warnings: &mut warnings,
942958
suggest_unsafe_block: true,
959+
rust_2024_unsafe_fns: &rust_2024_unsafe_fns,
943960
};
944961
visitor.visit_expr(&thir[expr]);
945962

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,8 @@ symbols! {
756756
end,
757757
env,
758758
env_CFG_RELEASE: env!("CFG_RELEASE"),
759+
env_remove_var,
760+
env_set_var,
759761
eprint_macro,
760762
eprintln_macro,
761763
eq,

library/std/src/env.rs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,6 @@ 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
///
328323
/// In multi-threaded programs, you must ensure that are no other threads
@@ -353,15 +348,18 @@ impl Error for VarError {
353348
/// use std::env;
354349
///
355350
/// let key = "KEY";
356-
/// env::set_var(key, "VALUE");
351+
/// unsafe {
352+
/// env::set_var(key, "VALUE");
353+
/// }
357354
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
358355
/// ```
356+
#[cfg_attr(not(any(bootstrap, test, doctest)), lang = "env_set_var")]
359357
#[stable(feature = "env", since = "1.0.0")]
360-
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
358+
pub unsafe fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
361359
_set_var(key.as_ref(), value.as_ref())
362360
}
363361

364-
fn _set_var(key: &OsStr, value: &OsStr) {
362+
unsafe fn _set_var(key: &OsStr, value: &OsStr) {
365363
os_imp::setenv(key, value).unwrap_or_else(|e| {
366364
panic!("failed to set environment variable `{key:?}` to `{value:?}`: {e}")
367365
})
@@ -371,11 +369,6 @@ fn _set_var(key: &OsStr, value: &OsStr) {
371369
///
372370
/// # Safety
373371
///
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-
///
379372
/// This function is safe to call in a single-threaded program.
380373
///
381374
/// In multi-threaded programs, you must ensure that are no other threads
@@ -403,22 +396,27 @@ fn _set_var(key: &OsStr, value: &OsStr) {
403396
///
404397
/// # Examples
405398
///
406-
/// ```
399+
/// ```no_run
407400
/// use std::env;
408401
///
409402
/// let key = "KEY";
410-
/// env::set_var(key, "VALUE");
403+
/// unsafe {
404+
/// env::set_var(key, "VALUE");
405+
/// }
411406
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
412407
///
413-
/// env::remove_var(key);
408+
/// unsafe {
409+
/// env::remove_var(key);
410+
/// }
414411
/// assert!(env::var(key).is_err());
415412
/// ```
413+
#[cfg_attr(not(any(bootstrap, test, doctest)), lang = "env_remove_var")]
416414
#[stable(feature = "env", since = "1.0.0")]
417-
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
415+
pub unsafe fn remove_var<K: AsRef<OsStr>>(key: K) {
418416
_remove_var(key.as_ref())
419417
}
420418

421-
fn _remove_var(key: &OsStr) {
419+
unsafe fn _remove_var(key: &OsStr) {
422420
os_imp::unsetenv(key)
423421
.unwrap_or_else(|e| panic!("failed to remove environment variable `{key:?}`: {e}"))
424422
}

library/std/src/process/tests.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,16 @@ fn test_capture_env_at_spawn() {
310310

311311
// This variable will not be present if the environment has already
312312
// been captured above.
313-
env::set_var("RUN_TEST_NEW_ENV2", "456");
313+
//
314+
// FIXME: This looks unsafe.
315+
unsafe {
316+
env::set_var("RUN_TEST_NEW_ENV2", "456");
317+
}
314318
let result = cmd.output().unwrap();
315-
env::remove_var("RUN_TEST_NEW_ENV2");
319+
// FIXME: This looks unsafe.
320+
unsafe {
321+
env::remove_var("RUN_TEST_NEW_ENV2");
322+
}
316323

317324
let output = String::from_utf8_lossy(&result.stdout).to_string();
318325

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

Lines changed: 5 additions & 9 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -667,19 +667,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
667667
.flatten()
668668
}
669669

670-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
670+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
671671
run_with_cstr(k.as_bytes(), &|k| {
672672
run_with_cstr(v.as_bytes(), &|v| {
673673
let _guard = ENV_LOCK.write();
674-
cvt(unsafe { libc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
674+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
675675
})
676676
})
677677
}
678678

679-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
679+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
680680
run_with_cstr(n.as_bytes(), &|nbuf| {
681681
let _guard = ENV_LOCK.write();
682-
cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop)
682+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
683683
})
684684
}
685685

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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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)