On target_os = "linux"
, ensure that only one Rust thread calls `lib… · model-checking/verify-rust-std@1530977 (original) (raw)
``
1
`+
cfg_if::cfg_if! {
`
``
2
`+
if #[cfg(target_os = "linux")] {
`
``
3
`+
/// Mitigation for https://github.com/rust-lang/rust/issues/126600
`
``
4
`+
///
`
``
5
`` +
/// On unix
(where libc::exit
may not be thread-safe), ensure that only one Rust thread
``
``
6
`` +
/// calls libc::exit
(or returns from main
) by calling this function before calling
``
``
7
`` +
/// libc::exit
(or returning from main
).
``
``
8
`+
///
`
``
9
`+
/// Technically not enough to ensure soundness, since other code directly calling
`
``
10
`+
/// libc::exit will still race with this.
`
``
11
`+
///
`
``
12
`` +
/// This function does not itself call libc::exit
. This is so it can also be used
``
``
13
`` +
/// to guard returning from main
.
``
``
14
`+
///
`
``
15
`+
/// This function will return only the first time it is called in a process.
`
``
16
`+
///
`
``
17
`+
/// * If it is called again on the same thread as the first call, it will abort.
`
``
18
`` +
/// * If it is called again on a different thread, it will thread::park()
in a loop
``
``
19
`+
/// (waiting for the process to exit).
`
``
20
`+
pub(crate) fn unique_thread_exit() {
`
``
21
`+
let this_thread_id = unsafe { libc::gettid() };
`
``
22
`+
debug_assert_ne!(this_thread_id, 0, "thread ID cannot be zero");
`
``
23
`+
#[cfg(target_has_atomic = "32")]
`
``
24
`+
{
`
``
25
`+
use crate::sync::atomic::{AtomicI32, Ordering};
`
``
26
`+
static EXITING_THREAD_ID: AtomicI32 = AtomicI32::new(0);
`
``
27
`+
match EXITING_THREAD_ID.compare_exchange(
`
``
28
`+
0,
`
``
29
`+
this_thread_id,
`
``
30
`+
Ordering::Relaxed,
`
``
31
`+
Ordering::Relaxed,
`
``
32
`+
) {
`
``
33
`+
Ok(_zero) => {
`
``
34
`` +
// This is the first thread to call unique_thread_exit
,
``
``
35
`+
// and this is the first time it is called.
`
``
36
`+
// Set EXITING_THREAD_ID to this thread's ID (done by the
`
``
37
`+
// compare_exchange) and return.
`
``
38
`+
}
`
``
39
`+
Err(id) if id == this_thread_id => {
`
``
40
`` +
// This is the first thread to call unique_thread_exit
,
``
``
41
`+
// but this is the second time it is called.
`
``
42
`+
// Abort the process.
`
``
43
`+
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
`
``
44
`+
}
`
``
45
`+
Err(_) => {
`
``
46
`` +
// This is not the first thread to call unique_thread_exit
.
``
``
47
`+
// Park until the process exits.
`
``
48
`+
loop {
`
``
49
`+
crate::thread::park();
`
``
50
`+
}
`
``
51
`+
}
`
``
52
`+
}
`
``
53
`+
}
`
``
54
`+
#[cfg(not(target_has_atomic = "32"))]
`
``
55
`+
{
`
``
56
`+
use crate::sync::{Mutex, PoisonError};
`
``
57
`+
static EXITING_THREAD_ID: Mutex = Mutex::new(0);
`
``
58
`+
let mut exiting_thread_id =
`
``
59
`+
EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner);
`
``
60
`+
if *exiting_thread_id == 0 {
`
``
61
`` +
// This is the first thread to call unique_thread_exit
,
``
``
62
`+
// and this is the first time it is called.
`
``
63
`+
// Set EXITING_THREAD_ID to this thread's ID and return.
`
``
64
`+
*exiting_thread_id = this_thread_id;
`
``
65
`+
} else if *exiting_thread_id == this_thread_id {
`
``
66
`` +
// This is the first thread to call unique_thread_exit
,
``
``
67
`+
// but this is the second time it is called.
`
``
68
`+
// Abort the process.
`
``
69
`+
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
`
``
70
`+
} else {
`
``
71
`` +
// This is not the first thread to call unique_thread_exit
.
``
``
72
`+
// Park until the process exits.
`
``
73
`+
drop(exiting_thread_id);
`
``
74
`+
loop {
`
``
75
`+
crate::thread::park();
`
``
76
`+
}
`
``
77
`+
}
`
``
78
`+
}
`
``
79
`+
}
`
``
80
`+
} else {
`
``
81
`+
/// Mitigation for https://github.com/rust-lang/rust/issues/126600
`
``
82
`+
///
`
``
83
`+
/// Mitigation is NOT implemented on this platform, either because this platform is not affected, or because mitigation is not yet implemented for this platform.
`
``
84
`+
pub(crate) fn unique_thread_exit() {
`
``
85
`` +
// Mitigation not required on platforms where exit
is thread-safe.
``
``
86
`+
}
`
``
87
`+
}
`
``
88
`+
}
`