Skip to content

Commit 4eda876

Browse files
authored
Rollup merge of rust-lang#124281 - RalfJung:win-tls, r=joboet
fix weak memory bug in TLS on Windows We need to store the `key` *after* we register the dtor. Now I hope there isn't also some other reason why we have to actually register the dtor last... `@joboet` is there a reason you picked this particular order in rust-lang#102655? Fixes rust-lang#123583
2 parents bef0f3f + d5d714b commit 4eda876

File tree

1 file changed

+22
-4
lines changed

1 file changed

+22
-4
lines changed

library/std/src/sys/pal/windows/thread_local_key.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,15 @@ impl StaticKey {
141141
panic!("out of TLS indexes");
142142
}
143143

144-
self.key.store(key + 1, Release);
145144
register_dtor(self);
146145

146+
// Release-storing the key needs to be the last thing we do.
147+
// This is because in `fn key()`, other threads will do an acquire load of the key,
148+
// and if that sees this write then it will entirely bypass the `InitOnce`. We thus
149+
// need to establish synchronization through `key`. In particular that acquire load
150+
// must happen-after the register_dtor above, to ensure the dtor actually runs!
151+
self.key.store(key + 1, Release);
152+
147153
let r = c::InitOnceComplete(self.once.get(), 0, ptr::null_mut());
148154
debug_assert_eq!(r, c::TRUE);
149155

@@ -313,17 +319,29 @@ unsafe fn run_dtors() {
313319
// Use acquire ordering to observe key initialization.
314320
let mut cur = DTORS.load(Acquire);
315321
while !cur.is_null() {
316-
let key = (*cur).key.load(Relaxed) - 1;
322+
let pre_key = (*cur).key.load(Acquire);
317323
let dtor = (*cur).dtor.unwrap();
324+
cur = (*cur).next.load(Relaxed);
325+
326+
// In StaticKey::init, we register the dtor before setting `key`.
327+
// So if one thread's `run_dtors` races with another thread executing `init` on the same
328+
// `StaticKey`, we can encounter a key of 0 here. That means this key was never
329+
// initialized in this thread so we can safely skip it.
330+
if pre_key == 0 {
331+
continue;
332+
}
333+
// If this is non-zero, then via the `Acquire` load above we synchronized with
334+
// everything relevant for this key. (It's not clear that this is needed, since the
335+
// release-acquire pair on DTORS also establishes synchronization, but better safe than
336+
// sorry.)
337+
let key = pre_key - 1;
318338

319339
let ptr = c::TlsGetValue(key);
320340
if !ptr.is_null() {
321341
c::TlsSetValue(key, ptr::null_mut());
322342
dtor(ptr as *mut _);
323343
any_run = true;
324344
}
325-
326-
cur = (*cur).next.load(Relaxed);
327345
}
328346

329347
if !any_run {

0 commit comments

Comments
 (0)