Skip to content

Commit 276b54e

Browse files
authoredMar 21, 2020
Rollup merge of #69955 - alexcrichton:stderr-infallible, r=sfackler
Fix abort-on-eprintln during process shutdown This commit fixes an issue where if `eprintln!` is used in a TLS destructor it can accidentally cause the process to abort. TLS destructors are executed after `main` returns on the main thread, and at this point we've also deinitialized global `Lazy` values like those which store the `Stderr` and `Stdout` internals. This means that despite handling TLS not being accessible in `eprintln!`, we will fail due to not being able to call `stderr()`. This means that we'll double-panic quickly because panicking also attempt to write to stderr. The fix here is to reimplement the global stderr handle to avoid the need for destruction. This avoids the need for `Lazy` as well as the hidden panic inside of the `stderr` function. Overall this should improve the robustness of printing errors and/or panics in weird situations, since the `stderr` accessor should be infallible in more situations.
2 parents 8deeac1 + 5edaa7e commit 276b54e

File tree

11 files changed

+149
-100
lines changed

11 files changed

+149
-100
lines changed
 

‎src/libstd/io/stdio.rs

+31-18
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::cell::RefCell;
66
use crate::fmt;
77
use crate::io::lazy::Lazy;
88
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
9-
use crate::sync::{Arc, Mutex, MutexGuard};
9+
use crate::sync::{Arc, Mutex, MutexGuard, Once};
1010
use crate::sys::stdio;
1111
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
1212
use crate::thread::LocalKey;
@@ -493,7 +493,11 @@ pub fn stdout() -> Stdout {
493493
Ok(stdout) => Maybe::Real(stdout),
494494
_ => Maybe::Fake,
495495
};
496-
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))))
496+
unsafe {
497+
let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))));
498+
ret.init();
499+
return ret;
500+
}
497501
}
498502
}
499503

@@ -520,7 +524,7 @@ impl Stdout {
520524
/// ```
521525
#[stable(feature = "rust1", since = "1.0.0")]
522526
pub fn lock(&self) -> StdoutLock<'_> {
523-
StdoutLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
527+
StdoutLock { inner: self.inner.lock() }
524528
}
525529
}
526530

@@ -581,7 +585,7 @@ impl fmt::Debug for StdoutLock<'_> {
581585
/// an error.
582586
#[stable(feature = "rust1", since = "1.0.0")]
583587
pub struct Stderr {
584-
inner: Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>>,
588+
inner: &'static ReentrantMutex<RefCell<Maybe<StderrRaw>>>,
585589
}
586590

587591
/// A locked reference to the `Stderr` handle.
@@ -639,19 +643,28 @@ pub struct StderrLock<'a> {
639643
/// ```
640644
#[stable(feature = "rust1", since = "1.0.0")]
641645
pub fn stderr() -> Stderr {
642-
static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new();
643-
return Stderr {
644-
inner: unsafe { INSTANCE.get(stderr_init).expect("cannot access stderr during shutdown") },
645-
};
646-
647-
fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> {
648-
// This must not reentrantly access `INSTANCE`
649-
let stderr = match stderr_raw() {
650-
Ok(stderr) => Maybe::Real(stderr),
651-
_ => Maybe::Fake,
652-
};
653-
Arc::new(ReentrantMutex::new(RefCell::new(stderr)))
654-
}
646+
// Note that unlike `stdout()` we don't use `Lazy` here which registers a
647+
// destructor. Stderr is not buffered nor does the `stderr_raw` type consume
648+
// any owned resources, so there's no need to run any destructors at some
649+
// point in the future.
650+
//
651+
// This has the added benefit of allowing `stderr` to be usable during
652+
// process shutdown as well!
653+
static INSTANCE: ReentrantMutex<RefCell<Maybe<StderrRaw>>> =
654+
unsafe { ReentrantMutex::new(RefCell::new(Maybe::Fake)) };
655+
656+
// When accessing stderr we need one-time initialization of the reentrant
657+
// mutex, followed by one-time detection of whether we actually have a
658+
// stderr handle or not. Afterwards we can just always use the now-filled-in
659+
// `INSTANCE` value.
660+
static INIT: Once = Once::new();
661+
INIT.call_once(|| unsafe {
662+
INSTANCE.init();
663+
if let Ok(stderr) = stderr_raw() {
664+
*INSTANCE.lock().borrow_mut() = Maybe::Real(stderr);
665+
}
666+
});
667+
return Stderr { inner: &INSTANCE };
655668
}
656669

657670
impl Stderr {
@@ -677,7 +690,7 @@ impl Stderr {
677690
/// ```
678691
#[stable(feature = "rust1", since = "1.0.0")]
679692
pub fn lock(&self) -> StderrLock<'_> {
680-
StderrLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) }
693+
StderrLock { inner: self.inner.lock() }
681694
}
682695
}
683696

‎src/libstd/sys/cloudabi/mutex.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,16 @@ pub struct ReentrantMutex {
5353
}
5454

5555
impl ReentrantMutex {
56-
pub unsafe fn uninitialized() -> ReentrantMutex {
56+
pub const unsafe fn uninitialized() -> ReentrantMutex {
5757
ReentrantMutex {
5858
lock: UnsafeCell::new(MaybeUninit::uninit()),
5959
recursion: UnsafeCell::new(MaybeUninit::uninit()),
6060
}
6161
}
6262

63-
pub unsafe fn init(&mut self) {
64-
self.lock = UnsafeCell::new(MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)));
65-
self.recursion = UnsafeCell::new(MaybeUninit::new(0));
63+
pub unsafe fn init(&self) {
64+
*self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0));
65+
*self.recursion.get() = MaybeUninit::new(0);
6666
}
6767

6868
pub unsafe fn try_lock(&self) -> bool {

‎src/libstd/sys/hermit/mutex.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ pub struct ReentrantMutex {
4646
}
4747

4848
impl ReentrantMutex {
49-
pub unsafe fn uninitialized() -> ReentrantMutex {
49+
pub const unsafe fn uninitialized() -> ReentrantMutex {
5050
ReentrantMutex { inner: ptr::null() }
5151
}
5252

5353
#[inline]
54-
pub unsafe fn init(&mut self) {
55-
let _ = abi::recmutex_init(&mut self.inner as *mut *const c_void);
54+
pub unsafe fn init(&self) {
55+
let _ = abi::recmutex_init(&self.inner as *const *const c_void as *mut _);
5656
}
5757

5858
#[inline]

‎src/libstd/sys/sgx/mutex.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl ReentrantMutex {
7575
}
7676

7777
#[inline]
78-
pub unsafe fn init(&mut self) {}
78+
pub unsafe fn init(&self) {}
7979

8080
#[inline]
8181
pub unsafe fn lock(&self) {

‎src/libstd/sys/unix/mutex.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ unsafe impl Send for ReentrantMutex {}
9292
unsafe impl Sync for ReentrantMutex {}
9393

9494
impl ReentrantMutex {
95-
pub unsafe fn uninitialized() -> ReentrantMutex {
95+
pub const unsafe fn uninitialized() -> ReentrantMutex {
9696
ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
9797
}
9898

99-
pub unsafe fn init(&mut self) {
99+
pub unsafe fn init(&self) {
100100
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
101101
let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
102102
debug_assert_eq!(result, 0);

‎src/libstd/sys/vxworks/mutex.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ unsafe impl Send for ReentrantMutex {}
9292
unsafe impl Sync for ReentrantMutex {}
9393

9494
impl ReentrantMutex {
95-
pub unsafe fn uninitialized() -> ReentrantMutex {
95+
pub const unsafe fn uninitialized() -> ReentrantMutex {
9696
ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
9797
}
9898

99-
pub unsafe fn init(&mut self) {
99+
pub unsafe fn init(&self) {
100100
let mut attr = MaybeUninit::<libc::pthread_mutexattr_t>::uninit();
101101
let result = libc::pthread_mutexattr_init(attr.as_mut_ptr());
102102
debug_assert_eq!(result, 0);

‎src/libstd/sys/wasm/mutex.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ impl Mutex {
4747
pub struct ReentrantMutex {}
4848

4949
impl ReentrantMutex {
50-
pub unsafe fn uninitialized() -> ReentrantMutex {
50+
pub const unsafe fn uninitialized() -> ReentrantMutex {
5151
ReentrantMutex {}
5252
}
5353

54-
pub unsafe fn init(&mut self) {}
54+
pub unsafe fn init(&self) {}
5555

5656
pub unsafe fn lock(&self) {}
5757

‎src/libstd/sys/wasm/mutex_atomics.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ unsafe impl Sync for ReentrantMutex {}
8080
// released when this recursion counter reaches 0.
8181

8282
impl ReentrantMutex {
83-
pub unsafe fn uninitialized() -> ReentrantMutex {
83+
pub const unsafe fn uninitialized() -> ReentrantMutex {
8484
ReentrantMutex { owner: AtomicU32::new(0), recursions: UnsafeCell::new(0) }
8585
}
8686

87-
pub unsafe fn init(&mut self) {
87+
pub unsafe fn init(&self) {
8888
// nothing to do...
8989
}
9090

‎src/libstd/sys/windows/mutex.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ impl Mutex {
109109
0 => {}
110110
n => return n as *mut _,
111111
}
112-
let mut re = box ReentrantMutex::uninitialized();
112+
let re = box ReentrantMutex::uninitialized();
113113
re.init();
114114
let re = Box::into_raw(re);
115115
match self.lock.compare_and_swap(0, re as usize, Ordering::SeqCst) {
@@ -157,11 +157,11 @@ unsafe impl Send for ReentrantMutex {}
157157
unsafe impl Sync for ReentrantMutex {}
158158

159159
impl ReentrantMutex {
160-
pub fn uninitialized() -> ReentrantMutex {
160+
pub const fn uninitialized() -> ReentrantMutex {
161161
ReentrantMutex { inner: UnsafeCell::new(MaybeUninit::uninit()) }
162162
}
163163

164-
pub unsafe fn init(&mut self) {
164+
pub unsafe fn init(&self) {
165165
c::InitializeCriticalSection((&mut *self.inner.get()).as_mut_ptr());
166166
}
167167

‎src/libstd/sys_common/remutex.rs

+51-63
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ use crate::marker;
33
use crate::ops::Deref;
44
use crate::panic::{RefUnwindSafe, UnwindSafe};
55
use crate::sys::mutex as sys;
6-
use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
76

87
/// A re-entrant mutual exclusion
98
///
109
/// This mutex will block *other* threads waiting for the lock to become
1110
/// available. The thread which has already locked the mutex can lock it
1211
/// multiple times without blocking, preventing a common source of deadlocks.
1312
pub struct ReentrantMutex<T> {
14-
inner: Box<sys::ReentrantMutex>,
15-
poison: poison::Flag,
13+
inner: sys::ReentrantMutex,
1614
data: T,
1715
}
1816

@@ -39,23 +37,30 @@ pub struct ReentrantMutexGuard<'a, T: 'a> {
3937
// funny underscores due to how Deref currently works (it disregards field
4038
// privacy).
4139
__lock: &'a ReentrantMutex<T>,
42-
__poison: poison::Guard,
4340
}
4441

4542
impl<T> !marker::Send for ReentrantMutexGuard<'_, T> {}
4643

4744
impl<T> ReentrantMutex<T> {
4845
/// Creates a new reentrant mutex in an unlocked state.
49-
pub fn new(t: T) -> ReentrantMutex<T> {
50-
unsafe {
51-
let mut mutex = ReentrantMutex {
52-
inner: box sys::ReentrantMutex::uninitialized(),
53-
poison: poison::Flag::new(),
54-
data: t,
55-
};
56-
mutex.inner.init();
57-
mutex
58-
}
46+
///
47+
/// # Unsafety
48+
///
49+
/// This function is unsafe because it is required that `init` is called
50+
/// once this mutex is in its final resting place, and only then are the
51+
/// lock/unlock methods safe.
52+
pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
53+
ReentrantMutex { inner: sys::ReentrantMutex::uninitialized(), data: t }
54+
}
55+
56+
/// Initializes this mutex so it's ready for use.
57+
///
58+
/// # Unsafety
59+
///
60+
/// Unsafe to call more than once, and must be called after this will no
61+
/// longer move in memory.
62+
pub unsafe fn init(&self) {
63+
self.inner.init();
5964
}
6065

6166
/// Acquires a mutex, blocking the current thread until it is able to do so.
@@ -70,7 +75,7 @@ impl<T> ReentrantMutex<T> {
7075
/// If another user of this mutex panicked while holding the mutex, then
7176
/// this call will return failure if the mutex would otherwise be
7277
/// acquired.
73-
pub fn lock(&self) -> LockResult<ReentrantMutexGuard<'_, T>> {
78+
pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
7479
unsafe { self.inner.lock() }
7580
ReentrantMutexGuard::new(&self)
7681
}
@@ -87,12 +92,8 @@ impl<T> ReentrantMutex<T> {
8792
/// If another user of this mutex panicked while holding the mutex, then
8893
/// this call will return failure if the mutex would otherwise be
8994
/// acquired.
90-
pub fn try_lock(&self) -> TryLockResult<ReentrantMutexGuard<'_, T>> {
91-
if unsafe { self.inner.try_lock() } {
92-
Ok(ReentrantMutexGuard::new(&self)?)
93-
} else {
94-
Err(TryLockError::WouldBlock)
95-
}
95+
pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
96+
if unsafe { self.inner.try_lock() } { Some(ReentrantMutexGuard::new(&self)) } else { None }
9697
}
9798
}
9899

@@ -108,11 +109,8 @@ impl<T> Drop for ReentrantMutex<T> {
108109
impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
109110
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110111
match self.try_lock() {
111-
Ok(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
112-
Err(TryLockError::Poisoned(err)) => {
113-
f.debug_struct("ReentrantMutex").field("data", &**err.get_ref()).finish()
114-
}
115-
Err(TryLockError::WouldBlock) => {
112+
Some(guard) => f.debug_struct("ReentrantMutex").field("data", &*guard).finish(),
113+
None => {
116114
struct LockedPlaceholder;
117115
impl fmt::Debug for LockedPlaceholder {
118116
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -127,11 +125,8 @@ impl<T: fmt::Debug + 'static> fmt::Debug for ReentrantMutex<T> {
127125
}
128126

129127
impl<'mutex, T> ReentrantMutexGuard<'mutex, T> {
130-
fn new(lock: &'mutex ReentrantMutex<T>) -> LockResult<ReentrantMutexGuard<'mutex, T>> {
131-
poison::map_result(lock.poison.borrow(), |guard| ReentrantMutexGuard {
132-
__lock: lock,
133-
__poison: guard,
134-
})
128+
fn new(lock: &'mutex ReentrantMutex<T>) -> ReentrantMutexGuard<'mutex, T> {
129+
ReentrantMutexGuard { __lock: lock }
135130
}
136131
}
137132

@@ -147,7 +142,6 @@ impl<T> Drop for ReentrantMutexGuard<'_, T> {
147142
#[inline]
148143
fn drop(&mut self) {
149144
unsafe {
150-
self.__lock.poison.done(&self.__poison);
151145
self.__lock.inner.unlock();
152146
}
153147
}
@@ -162,13 +156,17 @@ mod tests {
162156

163157
#[test]
164158
fn smoke() {
165-
let m = ReentrantMutex::new(());
159+
let m = unsafe {
160+
let m = ReentrantMutex::new(());
161+
m.init();
162+
m
163+
};
166164
{
167-
let a = m.lock().unwrap();
165+
let a = m.lock();
168166
{
169-
let b = m.lock().unwrap();
167+
let b = m.lock();
170168
{
171-
let c = m.lock().unwrap();
169+
let c = m.lock();
172170
assert_eq!(*c, ());
173171
}
174172
assert_eq!(*b, ());
@@ -179,15 +177,19 @@ mod tests {
179177

180178
#[test]
181179
fn is_mutex() {
182-
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
180+
let m = unsafe {
181+
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
182+
m.init();
183+
m
184+
};
183185
let m2 = m.clone();
184-
let lock = m.lock().unwrap();
186+
let lock = m.lock();
185187
let child = thread::spawn(move || {
186-
let lock = m2.lock().unwrap();
188+
let lock = m2.lock();
187189
assert_eq!(*lock.borrow(), 4950);
188190
});
189191
for i in 0..100 {
190-
let lock = m.lock().unwrap();
192+
let lock = m.lock();
191193
*lock.borrow_mut() += i;
192194
}
193195
drop(lock);
@@ -196,17 +198,21 @@ mod tests {
196198

197199
#[test]
198200
fn trylock_works() {
199-
let m = Arc::new(ReentrantMutex::new(()));
201+
let m = unsafe {
202+
let m = Arc::new(ReentrantMutex::new(()));
203+
m.init();
204+
m
205+
};
200206
let m2 = m.clone();
201-
let _lock = m.try_lock().unwrap();
202-
let _lock2 = m.try_lock().unwrap();
207+
let _lock = m.try_lock();
208+
let _lock2 = m.try_lock();
203209
thread::spawn(move || {
204210
let lock = m2.try_lock();
205-
assert!(lock.is_err());
211+
assert!(lock.is_none());
206212
})
207213
.join()
208214
.unwrap();
209-
let _lock3 = m.try_lock().unwrap();
215+
let _lock3 = m.try_lock();
210216
}
211217

212218
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
@@ -215,22 +221,4 @@ mod tests {
215221
*self.0.borrow_mut() = 42;
216222
}
217223
}
218-
219-
#[test]
220-
fn poison_works() {
221-
let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
222-
let mc = m.clone();
223-
let result = thread::spawn(move || {
224-
let lock = mc.lock().unwrap();
225-
*lock.borrow_mut() = 1;
226-
let lock2 = mc.lock().unwrap();
227-
*lock.borrow_mut() = 2;
228-
let _answer = Answer(lock2);
229-
panic!("What the answer to my lifetimes dilemma is?");
230-
})
231-
.join();
232-
assert!(result.is_err());
233-
let r = m.lock().err().unwrap().into_inner();
234-
assert_eq!(*r.borrow(), 42);
235-
}
236224
}

‎src/test/ui/eprint-on-tls-drop.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// run-pass
2+
// ignore-emscripten no processes
3+
4+
use std::cell::RefCell;
5+
use std::env;
6+
use std::process::Command;
7+
8+
fn main() {
9+
let name = "YOU_ARE_THE_TEST";
10+
if env::var(name).is_ok() {
11+
std::thread::spawn(|| {
12+
TLS.with(|f| f.borrow().ensure());
13+
})
14+
.join()
15+
.unwrap();
16+
} else {
17+
let me = env::current_exe().unwrap();
18+
let output = Command::new(&me).env(name, "1").output().unwrap();
19+
println!("{:?}", output);
20+
assert!(output.status.success());
21+
let stderr = String::from_utf8(output.stderr).unwrap();
22+
assert!(stderr.contains("hello new\n"));
23+
assert!(stderr.contains("hello drop\n"));
24+
}
25+
}
26+
27+
struct Stuff {
28+
_x: usize,
29+
}
30+
31+
impl Stuff {
32+
fn new() -> Self {
33+
eprintln!("hello new");
34+
Self { _x: 0 }
35+
}
36+
37+
fn ensure(&self) {}
38+
}
39+
40+
impl Drop for Stuff {
41+
fn drop(&mut self) {
42+
eprintln!("hello drop");
43+
}
44+
}
45+
46+
thread_local! {
47+
static TLS: RefCell<Stuff> = RefCell::new(Stuff::new());
48+
}

0 commit comments

Comments
 (0)
Please sign in to comment.