about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev+love@gmail.com>2022-11-23 06:40:21 +0900
committerGitHub <noreply@github.com>2022-11-23 06:40:21 +0900
commit2f506e6dd4eb4a3727aab8ba6349e80e3383dca9 (patch)
treec0ecc6c06b2125c002d8e2dab003a62b79c9d10c
parente221616639fb87de9dca21e252ee8a2565ec51d0 (diff)
parent3099dfdd9fc1a331eb9c53200b310fa1a06e1573 (diff)
downloadrust-2f506e6dd4eb4a3727aab8ba6349e80e3383dca9.tar.gz
rust-2f506e6dd4eb4a3727aab8ba6349e80e3383dca9.zip
Rollup merge of #101368 - thomcc:wintls-noinline, r=ChrisDenton
Forbid inlining `thread_local!`'s `__getit` function on Windows

Sadly, this will make things slower to avoid UB in an edge case, but it seems hard to avoid... and really whenever I look at this code I can't help but think we're asking for trouble.

It's pretty dodgy for us to leave this as a normal function rather than `#[inline(never)]`, given that if it *does* get inlined into a dynamically linked component, it's extremely unsafe (you get some other thread local, or if you're lucky, crash). Given that it's pretty rare for people to use dylibs on Windows, the fact that we haven't gotten bug reports about it isn't really that convincing. Ideally we'd come up with some kind of compiler solution (that avoids paying for this cost when static linking, or *at least* for use within the same crate...), but it's not clear what that looks like.

Oh, and because all this is only needed when we're implementing `thread_local!` with `#[thread_local]`, this patch adjusts the `cfg_attr` to be `all(windows, target_thread_local)` as well.

r? ``@ChrisDenton``

See also #84933, which is about improving the situation.
-rw-r--r--library/std/src/thread/local.rs25
1 files changed, 16 insertions, 9 deletions
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index 5d267891bb0..154b608c0dc 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -181,7 +181,8 @@ macro_rules! thread_local {
 macro_rules! __thread_local_inner {
     // used to generate the `LocalKey` value for const-initialized thread locals
     (@key $t:ty, const $init:expr) => {{
-        #[cfg_attr(not(windows), inline)] // see comments below
+        #[cfg_attr(not(all(windows, target_thread_local)), inline)] // see comments below
+        #[cfg_attr(all(windows, target_thread_local), inline(never))]
         #[deny(unsafe_op_in_unsafe_fn)]
         unsafe fn __getit(
             _init: $crate::option::Option<&mut $crate::option::Option<$t>>,
@@ -294,12 +295,17 @@ macro_rules! __thread_local_inner {
             fn __init() -> $t { $init }
 
             // When reading this function you might ask "why is this inlined
-            // everywhere other than Windows?", and that's a very reasonable
-            // question to ask. The short story is that it segfaults rustc if
-            // this function is inlined. The longer story is that Windows looks
-            // to not support `extern` references to thread locals across DLL
-            // boundaries. This appears to at least not be supported in the ABI
-            // that LLVM implements.
+            // everywhere other than Windows?", and "why must it not be inlined
+            // on Windows?" and these are very reasonable questions to ask.
+            //
+            // The short story is that Windows doesn't support referencing
+            // `#[thread_local]` across DLL boundaries. The slightly longer
+            // story is that each module (dll or exe) has its own separate set
+            // of static thread locals, so if you try and reference a
+            // `#[thread_local]` that comes from `crate1.dll` from within one of
+            // `crate2.dll`'s functions, then it might give you a completely
+            // different thread local than what you asked for (or it might just
+            // crash).
             //
             // Because of this we never inline on Windows, but we do inline on
             // other platforms (where external references to thread locals
@@ -314,8 +320,9 @@ macro_rules! __thread_local_inner {
             // Cargo question kinda). This means that, unfortunately, Windows
             // gets the pessimistic path for now where it's never inlined.
             //
-            // The issue of "should enable on Windows sometimes" is #84933
-            #[cfg_attr(not(windows), inline)]
+            // The issue of "should improve things on Windows" is #84933
+            #[cfg_attr(not(all(windows, target_thread_local)), inline)]
+            #[cfg_attr(all(windows, target_thread_local), inline(never))]
             unsafe fn __getit(
                 init: $crate::option::Option<&mut $crate::option::Option<$t>>,
             ) -> $crate::option::Option<&'static $t> {