1
1
cfg_if:: cfg_if! {
2
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
3
/// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
13
4
///
14
5
/// On glibc, `libc::exit` has been observed to not always be thread-safe.
@@ -30,28 +21,34 @@ cfg_if::cfg_if! {
30
21
/// (waiting for the process to exit).
31
22
#[ cfg_attr( any( test, doctest) , allow( dead_code) ) ]
32
23
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 <PThread >> = 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 => {
24
+ use crate :: ffi:: c_int;
25
+ use crate :: ptr;
26
+ use crate :: sync:: atomic:: AtomicPtr ;
27
+ use crate :: sync:: atomic:: Ordering :: { Acquire , Relaxed } ;
28
+
29
+ static EXITING_THREAD_ID : AtomicPtr <c_int> = AtomicPtr :: new( ptr:: null_mut( ) ) ;
30
+
31
+ // We use the address of `errno` as a cheap and safe way to identify
32
+ // threads. As the C standard mandates that `errno` must have thread
33
+ // storage duration, we can rely on its address not changing over the
34
+ // lifetime of the thread. Additionally, accesses to `errno` are
35
+ // async-signal-safe, so this function is available in all imaginable
36
+ // circumstances.
37
+ let this_thread_id = crate :: sys:: os:: errno_location( ) ;
38
+ match EXITING_THREAD_ID . compare_exchange( ptr:: null_mut( ) , this_thread_id, Acquire , Relaxed ) {
39
+ Ok ( _) => {
40
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 => {
41
+ // and this is the first time it is called. Continue exiting.
42
+ }
43
+ Err ( exiting_thread_id) if exiting_thread_id == this_thread_id => {
46
44
// This is the first thread to call `unique_thread_exit`,
47
45
// but this is the second time it is called.
48
46
// Abort the process.
49
47
core:: panicking:: panic_nounwind( "std::process::exit called re-entrantly" )
50
48
}
51
- Some ( _) => {
49
+ Err ( _) => {
52
50
// This is not the first thread to call `unique_thread_exit`.
53
51
// Pause until the process exits.
54
- drop( exiting_thread_id) ;
55
52
loop {
56
53
// Safety: libc::pause is safe to call.
57
54
unsafe { libc:: pause( ) ; }
0 commit comments