@@ -757,7 +757,87 @@ pub fn home_dir() -> Option<PathBuf> {
757
757
}
758
758
}
759
759
760
+ /// Mitigation for https://github.com/rust-lang/rust/issues/126600
761
+ ///
762
+ /// Ensure that only one Rust thread calls `libc::exit` (or returns from `main`) by
763
+ /// calling this function before calling `libc::exit` (or returning from `main`).
764
+ /// Technically not enough to ensure soundness, since other code directly calling
765
+ /// libc::exit will still race with this.
766
+ ///
767
+ /// *This function does not itself call `libc::exit`.* This is so it can also be used
768
+ /// to guard returning from `main`.
769
+ ///
770
+ /// This function will return only the first time it is called in a process.
771
+ ///
772
+ /// * If it is called again on the same thread as the first call, it will abort.
773
+ /// * If it is called again on a different thread, it will `thread::park()` in a loop
774
+ /// (waiting for the process to exit).
775
+ /// * If it is called in a situation where `std::thread::current()` fails, it will abort.
776
+ pub ( crate ) fn unique_thread_exit ( ) {
777
+ let this_thread_id =
778
+ crate :: thread:: try_current ( ) . unwrap_or_else ( || crate :: process:: abort ( ) ) . id ( ) . as_u64 ( ) . get ( ) ;
779
+ // Defense against refactors of `.as_u64()`
780
+ debug_assert_ne ! ( this_thread_id, 0 , "thread ID cannot be zero" ) ;
781
+ #[ cfg( target_has_atomic = "64" ) ]
782
+ {
783
+ use crate :: sync:: atomic:: { AtomicU64 , Ordering } ;
784
+ static EXITING_THREAD_ID : AtomicU64 = AtomicU64 :: new ( 0 ) ;
785
+ match EXITING_THREAD_ID . compare_exchange (
786
+ 0 ,
787
+ this_thread_id,
788
+ Ordering :: Relaxed ,
789
+ Ordering :: Relaxed ,
790
+ ) {
791
+ Ok ( _zero) => {
792
+ // This is the first thread to call `unique_thread_exit`,
793
+ // and this is the first time it is called.
794
+ // Set EXITING_THREAD_ID to this thread's ID (done by the
795
+ // compare_exchange) and return.
796
+ }
797
+ Err ( id) if id == this_thread_id => {
798
+ // This is the first thread to call `unique_thread_exit`,
799
+ // but this is the second time it is called.
800
+ // Abort the process.
801
+ crate :: process:: abort ( ) ;
802
+ }
803
+ Err ( _) => {
804
+ // This is not the first thread to call `unique_thread_exit`.
805
+ // Park until the process exits.
806
+ loop {
807
+ crate :: thread:: park ( ) ;
808
+ }
809
+ }
810
+ }
811
+ }
812
+ #[ cfg( not( target_has_atomic = "64" ) ) ]
813
+ {
814
+ use crate :: sync:: { Mutex , PoisonError } ;
815
+ static EXITING_THREAD_ID : Mutex < u64 > = Mutex :: new ( 0 ) ;
816
+ let mut exiting_thread_id =
817
+ EXITING_THREAD_ID . lock ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
818
+ if * exiting_thread_id == 0 {
819
+ // This is the first thread to call `unique_thread_exit`,
820
+ // and this is the first time it is called.
821
+ // Set EXITING_THREAD_ID to this thread's ID and return.
822
+ * exiting_thread_id = this_thread_id;
823
+ } else if * exiting_thread_id == this_thread_id {
824
+ // This is the first thread to call `unique_thread_exit`,
825
+ // but this is the second time it is called.
826
+ // Abort the process.
827
+ crate :: process:: abort ( ) ;
828
+ } else {
829
+ // This is not the first thread to call `unique_thread_exit`.
830
+ // Park until the process exits.
831
+ drop ( exiting_thread_id) ;
832
+ loop {
833
+ crate :: thread:: park ( ) ;
834
+ }
835
+ }
836
+ }
837
+ }
838
+
760
839
pub fn exit ( code : i32 ) -> ! {
840
+ unique_thread_exit ( ) ;
761
841
unsafe { libc:: exit ( code as c_int ) }
762
842
}
763
843
0 commit comments