From e71d06be1079db18c39b7088414380d94ce5a8f4 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Tue, 18 Jun 2024 12:43:06 -0500 Subject: [PATCH 01/12] On `target_os = "linux"`, ensure that only one Rust thread calls `libc::exit` or returns from `main`. --- library/std/src/rt.rs | 3 + library/std/src/sys/pal/common/exit_guard.rs | 88 ++++++++++++++++++++ library/std/src/sys/pal/common/mod.rs | 1 + library/std/src/sys/pal/unix/os.rs | 1 + 4 files changed, 93 insertions(+) create mode 100644 library/std/src/sys/pal/common/exit_guard.rs diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b03fa1c01f266..376bf3440693e 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -161,5 +161,8 @@ fn lang_start( argv, sigpipe, ); + // Guard against multple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + crate::sys::common::exit_guard::unique_thread_exit(); v } diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs new file mode 100644 index 0000000000000..3a0398a659831 --- /dev/null +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -0,0 +1,88 @@ +cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + /// Mitigation for https://github.com/rust-lang/rust/issues/126600 + /// + /// On `unix` (where `libc::exit` may not be thread-safe), ensure that only one Rust thread + /// calls `libc::exit` (or returns from `main`) by calling this function before calling + /// `libc::exit` (or returning from `main`). + /// + /// Technically not enough to ensure soundness, since other code directly calling + /// libc::exit will still race with this. + /// + /// *This function does not itself call `libc::exit`.* This is so it can also be used + /// to guard returning from `main`. + /// + /// This function will return only the first time it is called in a process. + /// + /// * If it is called again on the same thread as the first call, it will abort. + /// * If it is called again on a different thread, it will `thread::park()` in a loop + /// (waiting for the process to exit). + pub(crate) fn unique_thread_exit() { + let this_thread_id = unsafe { libc::gettid() }; + debug_assert_ne!(this_thread_id, 0, "thread ID cannot be zero"); + #[cfg(target_has_atomic = "32")] + { + use crate::sync::atomic::{AtomicI32, Ordering}; + static EXITING_THREAD_ID: AtomicI32 = AtomicI32::new(0); + match EXITING_THREAD_ID.compare_exchange( + 0, + this_thread_id, + Ordering::Relaxed, + Ordering::Relaxed, + ) { + Ok(_zero) => { + // This is the first thread to call `unique_thread_exit`, + // and this is the first time it is called. + // Set EXITING_THREAD_ID to this thread's ID (done by the + // compare_exchange) and return. + } + Err(id) if id == this_thread_id => { + // This is the first thread to call `unique_thread_exit`, + // but this is the second time it is called. + // Abort the process. + core::panicking::panic_nounwind("std::process::exit called re-entrantly") + } + Err(_) => { + // This is not the first thread to call `unique_thread_exit`. + // Park until the process exits. + loop { + crate::thread::park(); + } + } + } + } + #[cfg(not(target_has_atomic = "32"))] + { + use crate::sync::{Mutex, PoisonError}; + static EXITING_THREAD_ID: Mutex = Mutex::new(0); + let mut exiting_thread_id = + EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); + if *exiting_thread_id == 0 { + // This is the first thread to call `unique_thread_exit`, + // and this is the first time it is called. + // Set EXITING_THREAD_ID to this thread's ID and return. + *exiting_thread_id = this_thread_id; + } else if *exiting_thread_id == this_thread_id { + // This is the first thread to call `unique_thread_exit`, + // but this is the second time it is called. + // Abort the process. + core::panicking::panic_nounwind("std::process::exit called re-entrantly") + } else { + // This is not the first thread to call `unique_thread_exit`. + // Park until the process exits. + drop(exiting_thread_id); + loop { + crate::thread::park(); + } + } + } + } + } else { + /// Mitigation for https://github.com/rust-lang/rust/issues/126600 + /// + /// Mitigation is ***NOT*** implemented on this platform, either because this platform is not affected, or because mitigation is not yet implemented for this platform. + pub(crate) fn unique_thread_exit() { + // Mitigation not required on platforms where `exit` is thread-safe. + } + } +} diff --git a/library/std/src/sys/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs index 29fc0835d7666..cc1dceb63e2f5 100644 --- a/library/std/src/sys/pal/common/mod.rs +++ b/library/std/src/sys/pal/common/mod.rs @@ -11,6 +11,7 @@ #![allow(dead_code)] pub mod alloc; +pub mod exit_guard; pub mod small_c_string; #[cfg(test)] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 2e71ceceb58b1..3f598a095c156 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -758,6 +758,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { + crate::sys::common::exit_guard::unique_thread_exit(); unsafe { libc::exit(code as c_int) } } From bff35313972b0d3e248a39cf5a2d03d916ba56d2 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Thu, 20 Jun 2024 22:18:46 -0500 Subject: [PATCH 02/12] fix rustdoc URL --- library/std/src/sys/pal/common/exit_guard.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index 3a0398a659831..49d3b4a96f073 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -1,6 +1,6 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { - /// Mitigation for https://github.com/rust-lang/rust/issues/126600 + /// Mitigation for /// /// On `unix` (where `libc::exit` may not be thread-safe), ensure that only one Rust thread /// calls `libc::exit` (or returns from `main`) by calling this function before calling @@ -78,9 +78,10 @@ cfg_if::cfg_if! { } } } else { - /// Mitigation for https://github.com/rust-lang/rust/issues/126600 + /// Mitigation for /// - /// Mitigation is ***NOT*** implemented on this platform, either because this platform is not affected, or because mitigation is not yet implemented for this platform. + /// Mitigation is ***NOT*** implemented on this platform, either because this platform + /// is not affected, or because mitigation is not yet implemented for this platform. pub(crate) fn unique_thread_exit() { // Mitigation not required on platforms where `exit` is thread-safe. } From c36fdeb9a3da04839e0892e1351286881c848e39 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Thu, 20 Jun 2024 23:19:18 -0500 Subject: [PATCH 03/12] Don't perform mitigation for thread-unsafe libc::exit under Miri. 1. Miri's exit is thread-safe 2. Miri doesn't (yet) support `libc::gettid`, used in the implementation of the mitigation on Linux. --- library/std/src/sys/pal/common/exit_guard.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index 49d3b4a96f073..37c8f97e45d9d 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -1,5 +1,13 @@ cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(miri)] { + /// Mitigation for + /// + /// This mitigation is not necessary when running under Miri, so this function does nothing + /// when running under Miri. + pub(crate) fn unique_thread_exit() { + // Mitigation not required on Miri, where `exit` is thread-safe. + } + } else if #[cfg(target_os = "linux")] { /// Mitigation for /// /// On `unix` (where `libc::exit` may not be thread-safe), ensure that only one Rust thread From 5e83fafd88c2b2f8510f7b9db8c2748be7eb239c Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 3 Jul 2024 13:02:47 -0500 Subject: [PATCH 04/12] Use libc::pause instead of std::thread::park in wait-for-exit loop --- library/std/src/sys/pal/common/exit_guard.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index 37c8f97e45d9d..06f62941db746 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -10,9 +10,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "linux")] { /// Mitigation for /// - /// On `unix` (where `libc::exit` may not be thread-safe), ensure that only one Rust thread - /// calls `libc::exit` (or returns from `main`) by calling this function before calling - /// `libc::exit` (or returning from `main`). + /// On UNIX-like platforms (where `libc::exit` may not be thread-safe), ensure that only one + /// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before + /// calling `libc::exit` (or returning from `main`). /// /// Technically not enough to ensure soundness, since other code directly calling /// libc::exit will still race with this. @@ -23,7 +23,7 @@ cfg_if::cfg_if! { /// This function will return only the first time it is called in a process. /// /// * If it is called again on the same thread as the first call, it will abort. - /// * If it is called again on a different thread, it will `thread::park()` in a loop + /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). pub(crate) fn unique_thread_exit() { let this_thread_id = unsafe { libc::gettid() }; @@ -52,9 +52,10 @@ cfg_if::cfg_if! { } Err(_) => { // This is not the first thread to call `unique_thread_exit`. - // Park until the process exits. + // Pause until the process exits. loop { - crate::thread::park(); + // Safety: libc::pause is safe to call. + unsafe { libc::pause(); } } } } @@ -77,10 +78,12 @@ cfg_if::cfg_if! { core::panicking::panic_nounwind("std::process::exit called re-entrantly") } else { // This is not the first thread to call `unique_thread_exit`. + // Pause until the process exits. // Park until the process exits. drop(exiting_thread_id); loop { - crate::thread::park(); + // Safety: libc::pause is safe to call. + unsafe { libc::pause(); } } } } From 897fb6cb1ab30900a03a2806d312f9d22e43459c Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 3 Jul 2024 13:28:44 -0500 Subject: [PATCH 05/12] Use pthread_t instead of numeric thread id --- library/std/src/sys/pal/common/exit_guard.rs | 57 +++++--------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index 06f62941db746..0ea1faacee95d 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -26,60 +26,27 @@ cfg_if::cfg_if! { /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). pub(crate) fn unique_thread_exit() { - let this_thread_id = unsafe { libc::gettid() }; - debug_assert_ne!(this_thread_id, 0, "thread ID cannot be zero"); - #[cfg(target_has_atomic = "32")] - { - use crate::sync::atomic::{AtomicI32, Ordering}; - static EXITING_THREAD_ID: AtomicI32 = AtomicI32::new(0); - match EXITING_THREAD_ID.compare_exchange( - 0, - this_thread_id, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_zero) => { - // This is the first thread to call `unique_thread_exit`, - // and this is the first time it is called. - // Set EXITING_THREAD_ID to this thread's ID (done by the - // compare_exchange) and return. - } - Err(id) if id == this_thread_id => { - // This is the first thread to call `unique_thread_exit`, - // but this is the second time it is called. - // Abort the process. - core::panicking::panic_nounwind("std::process::exit called re-entrantly") - } - Err(_) => { - // This is not the first thread to call `unique_thread_exit`. - // Pause until the process exits. - loop { - // Safety: libc::pause is safe to call. - unsafe { libc::pause(); } - } - } - } - } - #[cfg(not(target_has_atomic = "32"))] - { - use crate::sync::{Mutex, PoisonError}; - static EXITING_THREAD_ID: Mutex = Mutex::new(0); - let mut exiting_thread_id = - EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); - if *exiting_thread_id == 0 { + let this_thread_id = unsafe { libc::pthread_self() }; + use crate::sync::{Mutex, PoisonError}; + static EXITING_THREAD_ID: Mutex> = Mutex::new(None); + let mut exiting_thread_id = + EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); + match *exiting_thread_id { + None => { // This is the first thread to call `unique_thread_exit`, // and this is the first time it is called. // Set EXITING_THREAD_ID to this thread's ID and return. - *exiting_thread_id = this_thread_id; - } else if *exiting_thread_id == this_thread_id { + *exiting_thread_id = Some(this_thread_id); + }, + Some(exiting_thread_id) if exiting_thread_id == this_thread_id => { // This is the first thread to call `unique_thread_exit`, // but this is the second time it is called. // Abort the process. core::panicking::panic_nounwind("std::process::exit called re-entrantly") - } else { + } + Some(_) => { // This is not the first thread to call `unique_thread_exit`. // Pause until the process exits. - // Park until the process exits. drop(exiting_thread_id); loop { // Safety: libc::pause is safe to call. From b512608275fed67cfccd05bea91d48d708ea7ae9 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 3 Jul 2024 13:33:32 -0500 Subject: [PATCH 06/12] Remove Miri special-case --- library/std/src/sys/pal/common/exit_guard.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index 0ea1faacee95d..eb2c14cdecc71 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -1,13 +1,5 @@ cfg_if::cfg_if! { - if #[cfg(miri)] { - /// Mitigation for - /// - /// This mitigation is not necessary when running under Miri, so this function does nothing - /// when running under Miri. - pub(crate) fn unique_thread_exit() { - // Mitigation not required on Miri, where `exit` is thread-safe. - } - } else if #[cfg(target_os = "linux")] { + if #[cfg(target_os = "linux")] { /// Mitigation for /// /// On UNIX-like platforms (where `libc::exit` may not be thread-safe), ensure that only one From b4149c6ad479cb35006355a1f1a3c0992a9f8b0e Mon Sep 17 00:00:00 2001 From: Zachary S Date: Wed, 3 Jul 2024 13:45:37 -0500 Subject: [PATCH 07/12] Move unique_thread_exit call to lang_start_internal so it is not in a generic function, and wrap it in `catch_unwind` --- library/std/src/rt.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 376bf3440693e..8a6f3fe291a75 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -144,6 +144,10 @@ fn lang_start_internal( rtabort!("drop of the panic payload panicked"); }); panic::catch_unwind(cleanup).map_err(rt_abort)?; + // Guard against multple threads calling `libc::exit` concurrently. + // See the documentation for `unique_thread_exit` for more information. + panic::catch_unwind(|| crate::sys::common::exit_guard::unique_thread_exit()) + .map_err(rt_abort)?; ret_code } @@ -161,8 +165,5 @@ fn lang_start( argv, sigpipe, ); - // Guard against multple threads calling `libc::exit` concurrently. - // See the documentation for `unique_thread_exit` for more information. - crate::sys::common::exit_guard::unique_thread_exit(); v } From 9de76e3201a557f0ea9b076c8b38629c5f2b60d0 Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:45:03 -0500 Subject: [PATCH 08/12] Update library/std/src/sys/pal/common/exit_guard.rs Co-authored-by: Ralf Jung --- library/std/src/sys/pal/common/exit_guard.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/pal/common/exit_guard.rs index eb2c14cdecc71..a1b0309b003ea 100644 --- a/library/std/src/sys/pal/common/exit_guard.rs +++ b/library/std/src/sys/pal/common/exit_guard.rs @@ -2,12 +2,14 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { /// Mitigation for /// - /// On UNIX-like platforms (where `libc::exit` may not be thread-safe), ensure that only one + /// On glibc, `libc::exit` has been observed to not always be thread-safe. + /// It is currently unclear whether that is a glibc bug or allowed by the standard. + /// To mitigate this problem, we ensure that only one /// Rust thread calls `libc::exit` (or returns from `main`) by calling this function before /// calling `libc::exit` (or returning from `main`). /// - /// Technically not enough to ensure soundness, since other code directly calling - /// libc::exit will still race with this. + /// Technically, this is not enough to ensure soundness, since other code directly calling + /// `libc::exit` will still race with this. /// /// *This function does not itself call `libc::exit`.* This is so it can also be used /// to guard returning from `main`. From a6093701437be909614bc49d63406dfaa63d3b13 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 5 Jul 2024 16:59:57 -0500 Subject: [PATCH 09/12] Move exit guard from sys::common::exit_guard to sys::exit_guard. --- library/std/src/rt.rs | 3 +-- library/std/src/sys/{pal/common => }/exit_guard.rs | 0 library/std/src/sys/mod.rs | 1 + library/std/src/sys/pal/common/mod.rs | 1 - library/std/src/sys/pal/unix/os.rs | 2 +- 5 files changed, 3 insertions(+), 4 deletions(-) rename library/std/src/sys/{pal/common => }/exit_guard.rs (100%) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 8a6f3fe291a75..c0a1c5f5594ae 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -146,8 +146,7 @@ fn lang_start_internal( panic::catch_unwind(cleanup).map_err(rt_abort)?; // Guard against multple threads calling `libc::exit` concurrently. // See the documentation for `unique_thread_exit` for more information. - panic::catch_unwind(|| crate::sys::common::exit_guard::unique_thread_exit()) - .map_err(rt_abort)?; + panic::catch_unwind(|| crate::sys::exit_guard::unique_thread_exit()).map_err(rt_abort)?; ret_code } diff --git a/library/std/src/sys/pal/common/exit_guard.rs b/library/std/src/sys/exit_guard.rs similarity index 100% rename from library/std/src/sys/pal/common/exit_guard.rs rename to library/std/src/sys/exit_guard.rs diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8aa35c40fe052..22ebc979bf7e4 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -3,6 +3,7 @@ /// descriptors. mod pal; +pub(crate) mod exit_guard; mod personality; pub mod backtrace; diff --git a/library/std/src/sys/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs index cc1dceb63e2f5..29fc0835d7666 100644 --- a/library/std/src/sys/pal/common/mod.rs +++ b/library/std/src/sys/pal/common/mod.rs @@ -11,7 +11,6 @@ #![allow(dead_code)] pub mod alloc; -pub mod exit_guard; pub mod small_c_string; #[cfg(test)] diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 3f598a095c156..397a0debe58c3 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -758,7 +758,7 @@ pub fn home_dir() -> Option { } pub fn exit(code: i32) -> ! { - crate::sys::common::exit_guard::unique_thread_exit(); + crate::sys::exit_guard::unique_thread_exit(); unsafe { libc::exit(code as c_int) } } From 5db165504ada6fde86f93d275a0d6380263ee381 Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 5 Jul 2024 17:59:46 -0500 Subject: [PATCH 10/12] Attempt to fix CI --- library/std/src/sys/exit_guard.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index a1b0309b003ea..ad6246cc83180 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -19,6 +19,7 @@ cfg_if::cfg_if! { /// * If it is called again on the same thread as the first call, it will abort. /// * If it is called again on a different thread, it will wait in a loop /// (waiting for the process to exit). + #[cfg_attr(any(test, doctest), allow(dead_code))] pub(crate) fn unique_thread_exit() { let this_thread_id = unsafe { libc::pthread_self() }; use crate::sync::{Mutex, PoisonError}; @@ -54,6 +55,7 @@ cfg_if::cfg_if! { /// /// Mitigation is ***NOT*** implemented on this platform, either because this platform /// is not affected, or because mitigation is not yet implemented for this platform. + #[cfg_attr(any(test, doctest), allow(dead_code))] pub(crate) fn unique_thread_exit() { // Mitigation not required on platforms where `exit` is thread-safe. } From 98010765f949307bf53ebb692e45e428ba318aa1 Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Sun, 7 Jul 2024 10:44:47 -0500 Subject: [PATCH 11/12] Move/change declaration of `mod exit_guard;` --- library/std/src/sys/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 22ebc979bf7e4..4092e6080d548 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -3,11 +3,11 @@ /// descriptors. mod pal; -pub(crate) mod exit_guard; mod personality; pub mod backtrace; pub mod cmath; +pub mod exit_guard; pub mod os_str; pub mod path; pub mod sync; From 8bcbab5dd1c6449c2acd3ea6a94b245c0936722d Mon Sep 17 00:00:00 2001 From: zachs18 <8355914+zachs18@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:19:25 -0500 Subject: [PATCH 12/12] Attempt to fix CI --- library/std/src/sys/exit_guard.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index ad6246cc83180..5a090f506661d 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -1,5 +1,14 @@ cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { + /// pthread_t is a pointer on some platforms, + /// so we wrap it in this to impl Send + Sync. + #[derive(Clone, Copy)] + #[repr(transparent)] + struct PThread(libc::pthread_t); + // Safety: pthread_t is safe to send between threads + unsafe impl Send for PThread {} + // Safety: pthread_t is safe to share between threads + unsafe impl Sync for PThread {} /// Mitigation for /// /// On glibc, `libc::exit` has been observed to not always be thread-safe. @@ -23,7 +32,7 @@ cfg_if::cfg_if! { pub(crate) fn unique_thread_exit() { let this_thread_id = unsafe { libc::pthread_self() }; use crate::sync::{Mutex, PoisonError}; - static EXITING_THREAD_ID: Mutex> = Mutex::new(None); + static EXITING_THREAD_ID: Mutex> = Mutex::new(None); let mut exiting_thread_id = EXITING_THREAD_ID.lock().unwrap_or_else(PoisonError::into_inner); match *exiting_thread_id { @@ -31,9 +40,9 @@ cfg_if::cfg_if! { // This is the first thread to call `unique_thread_exit`, // and this is the first time it is called. // Set EXITING_THREAD_ID to this thread's ID and return. - *exiting_thread_id = Some(this_thread_id); + *exiting_thread_id = Some(PThread(this_thread_id)); }, - Some(exiting_thread_id) if exiting_thread_id == this_thread_id => { + Some(exiting_thread_id) if exiting_thread_id.0 == this_thread_id => { // This is the first thread to call `unique_thread_exit`, // but this is the second time it is called. // Abort the process.