Skip to main content

std/sys/
exit.rs

1cfg_select! {
2    target_os = "linux" => {
3        /// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
4        ///
5        /// On glibc, `libc::exit` has been observed to not always be thread-safe.
6        /// It is currently unclear whether that is a glibc bug or allowed by the standard.
7        /// To mitigate this problem, we ensure that only one
8        /// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before
9        /// calling `libc::exit` (or returning from `main`).
10        ///
11        /// Technically, this is not enough to ensure soundness, since other code directly calling
12        /// `libc::exit` will still race with this.
13        ///
14        /// *This function does not itself call `libc::exit`.* This is so it can also be used
15        /// to guard returning from `main`.
16        ///
17        /// This function will return only the first time it is called in a process.
18        ///
19        /// * If it is called again on the same thread as the first call, it will abort.
20        /// * If it is called again on a different thread, it will wait in a loop
21        ///   (waiting for the process to exit).
22        pub fn unique_thread_exit() {
23            use crate::ffi::c_int;
24            use crate::ptr;
25            use crate::sync::atomic::AtomicPtr;
26            use crate::sync::atomic::Ordering::{Acquire, Relaxed};
27
28            static EXITING_THREAD_ID: AtomicPtr<c_int> = AtomicPtr::new(ptr::null_mut());
29
30            // We use the address of `errno` as a cheap and safe way to identify
31            // threads. As the C standard mandates that `errno` must have thread
32            // storage duration, we can rely on its address not changing over the
33            // lifetime of the thread. Additionally, accesses to `errno` are
34            // async-signal-safe, so this function is available in all imaginable
35            // circumstances.
36            let this_thread_id = crate::sys::io::errno_location();
37            match EXITING_THREAD_ID.compare_exchange(
38                ptr::null_mut(),
39                this_thread_id,
40                Acquire,
41                Relaxed,
42            ) {
43                Ok(_) => {
44                    // This is the first thread to call `unique_thread_exit`,
45                    // and this is the first time it is called. Continue exiting.
46                }
47                Err(exiting_thread_id) if exiting_thread_id == this_thread_id => {
48                    // This is the first thread to call `unique_thread_exit`,
49                    // but this is the second time it is called.
50                    // Abort the process.
51                    core::panicking::panic_nounwind("std::process::exit called re-entrantly")
52                }
53                Err(_) => {
54                    // This is not the first thread to call `unique_thread_exit`.
55                    // Pause until the process exits.
56                    loop {
57                        // Safety: libc::pause is safe to call.
58                        unsafe {
59                            libc::pause();
60                        }
61                    }
62                }
63            }
64        }
65    }
66    _ => {
67        /// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
68        ///
69        /// Mitigation is ***NOT*** implemented on this platform, either because this platform
70        /// is not affected, or because mitigation is not yet implemented for this platform.
71        #[cfg_attr(any(test, doctest), expect(dead_code))]
72        pub fn unique_thread_exit() {
73            // Mitigation not required on platforms where `exit` is thread-safe.
74        }
75    }
76}
77
78#[cfg(not(test))]
79cfg_select! {
80    any(target_family = "unix", target_os = "wasi") => {
81        // Used by rustc for checking the definitions of other function with the same symbol names
82        //
83        // See the `invalid_runtime_symbols_definitions` lint.
84        mod runtime_symbols {
85            unsafe extern "C" {
86                #[rustc_canonical_symbol]
87                fn exit(status: core::ffi::c_int) -> !;
88            }
89        }
90    }
91    _ => {}
92}
93
94pub fn exit(code: i32) -> ! {
95    cfg_select! {
96        target_os = "hermit" => unsafe { hermit_abi::exit(code) },
97        target_os = "linux" => unsafe {
98            unique_thread_exit();
99            libc::exit(code)
100        },
101        target_os = "motor" => moto_rt::process::exit(code),
102        all(target_vendor = "fortanix", target_env = "sgx") => {
103            crate::sys::pal::abi::exit_with_code(code as _)
104        }
105        target_os = "solid_asp3" => {
106            rtabort!("exit({}) called", code)
107        }
108        target_os = "teeos" => {
109            let _ = code;
110            panic!("TA should not call `exit`")
111        }
112        target_os = "uefi" => {
113            use r_efi::base::Status;
114
115            use crate::os::uefi::env;
116
117            if let (Some(boot_services), Some(handle)) =
118                (env::boot_services(), env::try_image_handle())
119            {
120                let boot_services = boot_services.cast::<r_efi::efi::BootServices>();
121                let _ = unsafe {
122                    ((*boot_services.as_ptr()).exit)(
123                        handle.as_ptr(),
124                        Status::from_usize(code as usize),
125                        0,
126                        crate::ptr::null_mut(),
127                    )
128                };
129            }
130            crate::intrinsics::abort()
131        }
132        any(target_family = "unix", target_os = "wasi") => unsafe {
133            libc::exit(code as crate::ffi::c_int)
134        },
135        target_os = "vexos" => {
136            let _ = code;
137
138            unsafe {
139                vex_sdk::vexSystemExitRequest();
140
141                loop {
142                    vex_sdk::vexTasksRun();
143                }
144            }
145        }
146        target_os = "windows" => unsafe { crate::sys::pal::c::ExitProcess(code as u32) },
147        target_os = "xous" => crate::os::xous::ffi::exit(code as u32),
148        _ => {
149            let _ = code;
150            crate::intrinsics::abort()
151        }
152    }
153}