Skip to content

Commit 4c6e77c

Browse files
committed
Introduce unsafe methods for mutating environment
1 parent 8769f4e commit 4c6e77c

File tree

8 files changed

+104
-49
lines changed

8 files changed

+104
-49
lines changed

library/std/src/env.rs

+71-2
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,43 @@ impl Error for VarError {
338338
/// ```
339339
#[stable(feature = "env", since = "1.0.0")]
340340
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
341+
// Safety: This isn't sound. See #27970 for details.
342+
unsafe { _set_var(key.as_ref(), value.as_ref()) }
343+
}
344+
345+
/// Sets the environment variable `key` to the value `value` for the currently running
346+
/// process.
347+
///
348+
/// # Panics
349+
///
350+
/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
351+
/// or the NUL character `'\0'`, or when `value` contains the NUL character.
352+
///
353+
/// # Safety
354+
///
355+
/// Some platforms only expose non-threadsafe APIs for altering the environment.
356+
/// On these platforms, you must ensure this method is not called when the
357+
/// program is multithreaded and any other thread could be reading or writing
358+
/// the environment.
359+
///
360+
/// # Examples
361+
///
362+
/// ```no_run
363+
/// #![feature(unsafe_env)]
364+
/// use std::env;
365+
///
366+
/// let key = "KEY";
367+
/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded!
368+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
369+
/// ```
370+
#[unstable(feature = "unsafe_env", issue = "none")]
371+
pub unsafe fn set<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
341372
_set_var(key.as_ref(), value.as_ref())
342373
}
343374

344-
fn _set_var(key: &OsStr, value: &OsStr) {
375+
// Safety: This can only be called when the program is single-threaded or when
376+
// no other thread touches the environment.
377+
unsafe fn _set_var(key: &OsStr, value: &OsStr) {
345378
os_imp::setenv(key, value).unwrap_or_else(|e| {
346379
panic!("failed to set environment variable `{:?}` to `{:?}`: {}", key, value, e)
347380
})
@@ -380,10 +413,46 @@ fn _set_var(key: &OsStr, value: &OsStr) {
380413
/// ```
381414
#[stable(feature = "env", since = "1.0.0")]
382415
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
416+
// Safety: This isn't sound. See #27970 for details.
417+
unsafe { _remove_var(key.as_ref()) }
418+
}
419+
420+
/// Removes an environment variable from the environment of the currently running process.
421+
///
422+
/// # Panics
423+
///
424+
/// This function may panic if `key` is empty, contains an ASCII equals sign
425+
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
426+
/// character.
427+
///
428+
/// # Safety
429+
///
430+
/// Some platforms only expose non-threadsafe APIs for altering the environment.
431+
/// On these platforms, you must ensure this method is not called when the
432+
/// program is multithreaded and any other thread could be reading or writing
433+
/// the environment.
434+
///
435+
/// # Examples
436+
///
437+
/// ```no_run
438+
/// #![feature(unsafe_env)]
439+
/// use std::env;
440+
///
441+
/// let key = "KEY";
442+
/// unsafe { env::set(key, "VALUE"); } // Make sure you're single-threaded!
443+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
444+
///
445+
/// unsafe { env::unset(key); } // Make sure you're single-threaded!
446+
/// assert!(env::var(key).is_err());
447+
/// ```
448+
#[unstable(feature = "unsafe_env", issue = "none")]
449+
pub unsafe fn unset<K: AsRef<OsStr>>(key: K) {
383450
_remove_var(key.as_ref())
384451
}
385452

386-
fn _remove_var(key: &OsStr) {
453+
// Safety: This can only be called when the program is single-threaded or when
454+
// no other thread touches the environment.
455+
unsafe fn _remove_var(key: &OsStr) {
387456
os_imp::unsetenv(key)
388457
.unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", key, e))
389458
}

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

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

147-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
148-
unsafe {
149-
let (k, v) = (k.to_owned(), v.to_owned());
150-
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
151-
}
147+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
148+
let (k, v) = (k.to_owned(), v.to_owned());
149+
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
152150
Ok(())
153151
}
154152

155-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
156-
unsafe {
157-
ENV.as_ref().unwrap().lock().unwrap().remove(k);
158-
}
153+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
154+
ENV.as_ref().unwrap().lock().unwrap().remove(k);
159155
Ok(())
160156
}
161157

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

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

113-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
113+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
114114
let (k, v) = (k.to_owned(), v.to_owned());
115115
create_env_store().lock().unwrap().insert(k, v);
116116
Ok(())
117117
}
118118

119-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
119+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
120120
if let Some(env) = get_env_store() {
121121
env.lock().unwrap().remove(k);
122122
}

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

+6-10
Original file line numberDiff line numberDiff line change
@@ -151,23 +151,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
151151
}
152152
}
153153

154-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
154+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
155155
let k = CString::new(k.as_bytes())?;
156156
let v = CString::new(v.as_bytes())?;
157157

158-
unsafe {
159-
let _guard = ENV_LOCK.write();
160-
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
161-
}
158+
let _guard = ENV_LOCK.write();
159+
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
162160
}
163161

164-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
162+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
165163
let nbuf = CString::new(n.as_bytes())?;
166164

167-
unsafe {
168-
let _guard = ENV_LOCK.write();
169-
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
170-
}
165+
let _guard = ENV_LOCK.write();
166+
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
171167
}
172168

173169
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this

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

+8-10
Original file line numberDiff line numberDiff line change
@@ -538,23 +538,21 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
538538
}
539539
}
540540

541-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
541+
// Safety: This can only be called when the program is single-threaded.
542+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
542543
let k = CString::new(k.as_bytes())?;
543544
let v = CString::new(v.as_bytes())?;
544545

545-
unsafe {
546-
let _guard = ENV_LOCK.write();
547-
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
548-
}
546+
let _guard = ENV_LOCK.write();
547+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
549548
}
550549

551-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
550+
// Safety: This can only be called when the program is single-threaded.
551+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
552552
let nbuf = CString::new(n.as_bytes())?;
553553

554-
unsafe {
555-
let _guard = ENV_LOCK.write();
556-
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
557-
}
554+
let _guard = ENV_LOCK.write();
555+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
558556
}
559557

560558
#[cfg(not(target_os = "espidf"))]

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
8080
None
8181
}
8282

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

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

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

+6-10
Original file line numberDiff line numberDiff line change
@@ -188,23 +188,19 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
188188
}
189189
}
190190

191-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
191+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
192192
let k = CString::new(k.as_bytes())?;
193193
let v = CString::new(v.as_bytes())?;
194194

195-
unsafe {
196-
let _guard = env_lock();
197-
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
198-
}
195+
let _guard = env_lock();
196+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
199197
}
200198

201-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
199+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
202200
let nbuf = CString::new(n.as_bytes())?;
203201

204-
unsafe {
205-
let _guard = env_lock();
206-
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
207-
}
202+
let _guard = env_lock();
203+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
208204
}
209205

210206
pub fn temp_dir() -> PathBuf {

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,16 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
262262
.ok()
263263
}
264264

265-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
265+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
266266
let k = to_u16s(k)?;
267267
let v = to_u16s(v)?;
268268

269-
cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
269+
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
270270
}
271271

272-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
272+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
273273
let v = to_u16s(n)?;
274-
cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
274+
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
275275
}
276276

277277
pub fn temp_dir() -> PathBuf {

0 commit comments

Comments
 (0)