Skip to content

Commit 4d3cffa

Browse files
committed
std: Fix unsoundness of std::thread::ScopedKey
Currently the compiler has no knowledge of `#[thread_local]` which forces users to take on two burdens of unsafety: * The lifetime of the borrow of a `#[thread_local]` static is **not** `'static` * Types in `static`s are required to be `Sync` The thread-local modules mostly curb these facets of unsafety by only allowing very limited scopes of borrows as well as allowing all types to be stored in a thread-local key (regardless of whether they are `Sync`) through an `unsafe impl`. Unfortunately these measures have the consequence of being able to take the address of the key itself and send it to another thread, allowing the same key to be accessed from two different threads. This is clearly unsafe, and this commit fixes this problem with the same trick used by `LocalKey`, which is to have an indirect function call to find the address of the *current thread's* thread local. This way the address of thread local keys can safely be sent among threads as their lifetime truly is `'static`. This commit will reduce the performance of cross-crate scoped thread locals as it now requires an indirect function call, but this can likely be overcome in a future commit. Closes #25894
1 parent a49ae5b commit 4d3cffa

File tree

2 files changed

+48
-32
lines changed

2 files changed

+48
-32
lines changed

src/libstd/thread/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ pub use self::local::{LocalKey, LocalKeyState};
217217
pub use self::scoped_tls::ScopedKey;
218218

219219
#[doc(hidden)] pub use self::local::__KeyInner as __LocalKeyInner;
220+
#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner;
220221

221222
////////////////////////////////////////////////////////////////////////////////
222223
// Builder

src/libstd/thread/scoped_tls.rs

+47-32
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343

4444
use prelude::v1::*;
4545

46+
#[doc(hidden)]
47+
pub use self::imp::KeyInner as __KeyInner;
48+
4649
/// Type representing a thread local storage key corresponding to a reference
4750
/// to the type parameter `T`.
4851
///
@@ -53,7 +56,7 @@ use prelude::v1::*;
5356
#[unstable(feature = "scoped_tls",
5457
reason = "scoped TLS has yet to have wide enough use to fully consider \
5558
stabilizing its interface")]
56-
pub struct ScopedKey<T> { inner: imp::KeyInner<T> }
59+
pub struct ScopedKey<T> { inner: fn() -> &'static imp::KeyInner<T> }
5760

5861
/// Declare a new scoped thread local storage key.
5962
///
@@ -64,51 +67,60 @@ pub struct ScopedKey<T> { inner: imp::KeyInner<T> }
6467
/// information.
6568
#[macro_export]
6669
#[allow_internal_unstable]
67-
#[cfg(not(no_elf_tls))]
6870
macro_rules! scoped_thread_local {
6971
(static $name:ident: $t:ty) => (
70-
#[cfg_attr(not(any(windows,
71-
target_os = "android",
72-
target_os = "ios",
73-
target_os = "openbsd",
74-
target_arch = "aarch64")),
75-
thread_local)]
7672
static $name: ::std::thread::ScopedKey<$t> =
77-
::std::thread::ScopedKey::new();
73+
__scoped_thread_local_inner!($t);
7874
);
7975
(pub static $name:ident: $t:ty) => (
80-
#[cfg_attr(not(any(windows,
81-
target_os = "android",
82-
target_os = "ios",
83-
target_os = "openbsd",
84-
target_arch = "aarch64")),
85-
thread_local)]
8676
pub static $name: ::std::thread::ScopedKey<$t> =
87-
::std::thread::ScopedKey::new();
77+
__scoped_thread_local_inner!($t);
8878
);
8979
}
9080

81+
#[doc(hidden)]
82+
#[unstable(feature = "thread_local_internals",
83+
reason = "should not be necessary")]
9184
#[macro_export]
9285
#[allow_internal_unstable]
9386
#[cfg(no_elf_tls)]
94-
macro_rules! scoped_thread_local {
95-
(static $name:ident: $t:ty) => (
96-
static $name: ::std::thread::ScopedKey<$t> =
97-
::std::thread::ScopedKey::new();
98-
);
99-
(pub static $name:ident: $t:ty) => (
100-
pub static $name: ::std::thread::ScopedKey<$t> =
101-
::std::thread::ScopedKey::new();
102-
);
87+
macro_rules! __scoped_thread_local_inner {
88+
($t:ty) => {{
89+
static _KEY: ::std::thread::__ScopedKeyInner<$t> =
90+
::std::thread::__ScopedKeyInner::new();
91+
fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY }
92+
::std::thread::ScopedKey::new(_getit)
93+
}}
94+
}
95+
96+
#[doc(hidden)]
97+
#[unstable(feature = "thread_local_internals",
98+
reason = "should not be necessary")]
99+
#[macro_export]
100+
#[allow_internal_unstable]
101+
#[cfg(not(no_elf_tls))]
102+
macro_rules! __scoped_thread_local_inner {
103+
($t:ty) => {{
104+
#[cfg_attr(not(any(windows,
105+
target_os = "android",
106+
target_os = "ios",
107+
target_os = "openbsd",
108+
target_arch = "aarch64")),
109+
thread_local)]
110+
static _KEY: ::std::thread::__ScopedKeyInner<$t> =
111+
::std::thread::__ScopedKeyInner::new();
112+
fn _getit() -> &'static ::std::thread::__ScopedKeyInner<$t> { &_KEY }
113+
::std::thread::ScopedKey::new(_getit)
114+
}}
103115
}
104116

105117
#[unstable(feature = "scoped_tls",
106118
reason = "scoped TLS has yet to have wide enough use to fully consider \
107119
stabilizing its interface")]
108120
impl<T> ScopedKey<T> {
109121
#[doc(hidden)]
110-
pub const fn new() -> ScopedKey<T> {
111-
ScopedKey { inner: imp::KeyInner::new() }
122+
pub const fn new(inner: fn() -> &'static imp::KeyInner<T>) -> ScopedKey<T> {
123+
ScopedKey { inner: inner }
112124
}
113125

114126
/// Inserts a value into this scoped thread local storage slot for a
@@ -153,13 +165,14 @@ impl<T> ScopedKey<T> {
153165
}
154166
}
155167

168+
let inner = (self.inner)();
156169
let prev = unsafe {
157-
let prev = self.inner.get();
158-
self.inner.set(t as *const T as *mut T);
170+
let prev = inner.get();
171+
inner.set(t as *const T as *mut T);
159172
prev
160173
};
161174

162-
let _reset = Reset { key: &self.inner, val: prev };
175+
let _reset = Reset { key: inner, val: prev };
163176
cb()
164177
}
165178

@@ -186,7 +199,7 @@ impl<T> ScopedKey<T> {
186199
F: FnOnce(&T) -> R
187200
{
188201
unsafe {
189-
let ptr = self.inner.get();
202+
let ptr = (self.inner)().get();
190203
assert!(!ptr.is_null(), "cannot access a scoped thread local \
191204
variable without calling `set` first");
192205
cb(&*ptr)
@@ -195,7 +208,7 @@ impl<T> ScopedKey<T> {
195208

196209
/// Test whether this TLS key has been `set` for the current thread.
197210
pub fn is_set(&'static self) -> bool {
198-
unsafe { !self.inner.get().is_null() }
211+
unsafe { !(self.inner)().get().is_null() }
199212
}
200213
}
201214

@@ -205,6 +218,7 @@ impl<T> ScopedKey<T> {
205218
target_os = "openbsd",
206219
target_arch = "aarch64",
207220
no_elf_tls)))]
221+
#[doc(hidden)]
208222
mod imp {
209223
use std::cell::Cell;
210224

@@ -227,6 +241,7 @@ mod imp {
227241
target_os = "openbsd",
228242
target_arch = "aarch64",
229243
no_elf_tls))]
244+
#[doc(hidden)]
230245
mod imp {
231246
use prelude::v1::*;
232247

0 commit comments

Comments
 (0)