Auto merge of #126606 - zachs18:patch-2, r=joboet · model-checking/verify-rust-std@206678c (original) (raw)
``
1
`+
cfg_if::cfg_if! {
`
``
2
`+
if #[cfg(target_os = "linux")] {
`
``
3
`+
/// pthread_t is a pointer on some platforms,
`
``
4
`+
/// so we wrap it in this to impl Send + Sync.
`
``
5
`+
#[derive(Clone, Copy)]
`
``
6
`+
#[repr(transparent)]
`
``
7
`+
struct PThread(libc::pthread_t);
`
``
8
`+
// Safety: pthread_t is safe to send between threads
`
``
9
`+
unsafe impl Send for PThread {}
`
``
10
`+
// Safety: pthread_t is safe to share between threads
`
``
11
`+
unsafe impl Sync for PThread {}
`
``
12
`+
/// Mitigation for https://github.com/rust-lang/rust/issues/126600
`
``
13
`+
///
`
``
14
`` +
/// On glibc, libc::exit
has been observed to not always be thread-safe.
``
``
15
`+
/// It is currently unclear whether that is a glibc bug or allowed by the standard.
`
``
16
`+
/// To mitigate this problem, we ensure that only one
`
``
17
`` +
/// Rust thread calls libc::exit
(or returns from main
) by calling this function before
``
``
18
`` +
/// calling libc::exit
(or returning from main
).
``
``
19
`+
///
`
``
20
`+
/// Technically, this is not enough to ensure soundness, since other code directly calling
`
``
21
`` +
/// libc::exit
will still race with this.
``
``
22
`+
///
`
``
23
`` +
/// This function does not itself call libc::exit
. This is so it can also be used
``
``
24
`` +
/// to guard returning from main
.
``
``
25
`+
///
`
``
26
`+
/// This function will return only the first time it is called in a process.
`
``
27
`+
///
`
``
28
`+
/// * If it is called again on the same thread as the first call, it will abort.
`
``
29
`+
/// * If it is called again on a different thread, it will wait in a loop
`
``
30
`+
/// (waiting for the process to exit).
`
``
31
`+
#[cfg_attr(any(test, doctest), allow(dead_code))]
`
``
32
`+
pub(crate) fn unique_thread_exit() {
`
``
33
`+
let this_thread_id = unsafe { libc::pthread_self() };
`
``
34
`+
use crate::sync::{Mutex, PoisonError};
`
``
35
`+
static EXITING_THREAD_ID: Mutex<Option> = Mutex::new(None);
`
``
36
`+
let mut exiting_thread_id =
`
``
37
`+
EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner);
`
``
38
`+
match *exiting_thread_id {
`
``
39
`+
None => {
`
``
40
`` +
// This is the first thread to call unique_thread_exit
,
``
``
41
`+
// and this is the first time it is called.
`
``
42
`+
// Set EXITING_THREAD_ID to this thread's ID and return.
`
``
43
`+
*exiting_thread_id = Some(PThread(this_thread_id));
`
``
44
`+
},
`
``
45
`+
Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => {
`
``
46
`` +
// This is the first thread to call unique_thread_exit
,
``
``
47
`+
// but this is the second time it is called.
`
``
48
`+
// Abort the process.
`
``
49
`+
core::panicking::panic_nounwind("std::process::exit called re-entrantly")
`
``
50
`+
}
`
``
51
`+
Some(_) => {
`
``
52
`` +
// This is not the first thread to call unique_thread_exit
.
``
``
53
`+
// Pause until the process exits.
`
``
54
`+
drop(exiting_thread_id);
`
``
55
`+
loop {
`
``
56
`+
// Safety: libc::pause is safe to call.
`
``
57
`+
unsafe { libc::pause(); }
`
``
58
`+
}
`
``
59
`+
}
`
``
60
`+
}
`
``
61
`+
}
`
``
62
`+
} else {
`
``
63
`+
/// Mitigation for https://github.com/rust-lang/rust/issues/126600
`
``
64
`+
///
`
``
65
`+
/// Mitigation is NOT implemented on this platform, either because this platform
`
``
66
`+
/// is not affected, or because mitigation is not yet implemented for this platform.
`
``
67
`+
#[cfg_attr(any(test, doctest), allow(dead_code))]
`
``
68
`+
pub(crate) fn unique_thread_exit() {
`
``
69
`` +
// Mitigation not required on platforms where exit
is thread-safe.
``
``
70
`+
}
`
``
71
`+
}
`
``
72
`+
}
`