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(ptr::null_mut(), this_thread_id, Acquire, Relaxed) {
38                Ok(_) => {
39                    // This is the first thread to call `unique_thread_exit`,
40                    // and this is the first time it is called. Continue exiting.
41                }
42                Err(exiting_thread_id) if exiting_thread_id == this_thread_id => {
43                    // This is the first thread to call `unique_thread_exit`,
44                    // but this is the second time it is called.
45                    // Abort the process.
46                    core::panicking::panic_nounwind("std::process::exit called re-entrantly")
47                }
48                Err(_) => {
49                    // This is not the first thread to call `unique_thread_exit`.
50                    // Pause until the process exits.
51                    loop {
52                        // Safety: libc::pause is safe to call.
53                        unsafe { libc::pause(); }
54                    }
55                }
56            }
57        }
58    }
59    _ => {
60        /// Mitigation for <https://github.com/rust-lang/rust/issues/126600>
61        ///
62        /// Mitigation is ***NOT*** implemented on this platform, either because this platform
63        /// is not affected, or because mitigation is not yet implemented for this platform.
64        #[cfg_attr(any(test, doctest), expect(dead_code))]
65        pub fn unique_thread_exit() {
66            // Mitigation not required on platforms where `exit` is thread-safe.
67        }
68    }
69}
70
71#[cfg(not(test))]
72cfg_select! {
73    any(
74        target_family = "unix",
75        target_os = "wasi",
76    ) => {
77        // Used by rustc for checking the definitions of other function with the same symbol names
78        //
79        // See the `invalid_runtime_symbols_definitions` lint.
80        mod runtime_symbols {
81            unsafe extern "C" {
82                #[lang = "exit_fn"]
83                fn exit(status: core::ffi::c_int) -> !;
84            }
85        }
86    }
87    _ => {}
88}
89
90pub fn exit(code: i32) -> ! {
91    cfg_select! {
92        target_os = "hermit" => {
93            unsafe { hermit_abi::exit(code) }
94        }
95        target_os = "linux" => {
96            unsafe {
97                unique_thread_exit();
98                libc::exit(code)
99            }
100        }
101        target_os = "motor" => {
102            moto_rt::process::exit(code)
103        }
104        all(target_vendor = "fortanix", target_env = "sgx") => {
105            crate::sys::pal::abi::exit_with_code(code as _)
106        }
107        target_os = "solid_asp3" => {
108            rtabort!("exit({}) called", code)
109        }
110        target_os = "teeos" => {
111            let _ = code;
112            panic!("TA should not call `exit`")
113        }
114        target_os = "uefi" => {
115            use r_efi::base::Status;
116
117            use crate::os::uefi::env;
118
119            if let (Some(boot_services), Some(handle)) =
120                (env::boot_services(), env::try_image_handle())
121            {
122                let boot_services = boot_services.cast::<r_efi::efi::BootServices>();
123                let _ = unsafe {
124                    ((*boot_services.as_ptr()).exit)(
125                        handle.as_ptr(),
126                        Status::from_usize(code as usize),
127                        0,
128                        crate::ptr::null_mut(),
129                    )
130                };
131            }
132            crate::intrinsics::abort()
133        }
134        any(
135            target_family = "unix",
136            target_os = "wasi",
137        ) => {
138            unsafe { libc::exit(code as crate::ffi::c_int) }
139        }
140        target_os = "vexos" => {
141            let _ = code;
142
143            unsafe {
144                vex_sdk::vexSystemExitRequest();
145
146                loop {
147                    vex_sdk::vexTasksRun();
148                }
149            }
150        }
151        target_os = "windows" => {
152            unsafe { crate::sys::pal::c::ExitProcess(code as u32) }
153        }
154        target_os = "xous" => {
155            crate::os::xous::ffi::exit(code as u32)
156        }
157        _ => {
158            let _ = code;
159            crate::intrinsics::abort()
160        }
161    }
162}