Skip to content

Commit 982b101

Browse files
committed
Auto merge of rust-lang#116402 - joboet:global_alloc_tls_unsoundness, r=thomcc,workingjubilee
Panic when the global allocator tries to register a TLS destructor Using a `RefCell` avoids the undefined behaviour encountered in rust-lang#116390 and reduces the amount of `unsafe` code in the codebase.
2 parents fa6d1e7 + 88efb1b commit 982b101

File tree

5 files changed

+51
-29
lines changed

5 files changed

+51
-29
lines changed

library/std/src/sys/hermit/thread_local_dtor.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,25 @@
55
// The this solution works like the implementation of macOS and
66
// doesn't additional OS support
77

8-
use crate::mem;
8+
use crate::cell::RefCell;
99

1010
#[thread_local]
11-
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
11+
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
1212

1313
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
14-
let list = &mut DTORS;
15-
list.push((t, dtor));
14+
match DTORS.try_borrow_mut() {
15+
Ok(mut dtors) => dtors.push((t, dtor)),
16+
Err(_) => rtabort!("global allocator may not use TLS"),
17+
}
1618
}
1719

1820
// every thread call this function to run through all possible destructors
1921
pub unsafe fn run_dtors() {
20-
let mut list = mem::take(&mut DTORS);
22+
let mut list = DTORS.take();
2123
while !list.is_empty() {
2224
for (ptr, dtor) in list {
2325
dtor(ptr);
2426
}
25-
list = mem::take(&mut DTORS);
27+
list = DTORS.take();
2628
}
2729
}

library/std/src/sys/solid/thread_local_dtor.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
// Simplify dtor registration by using a list of destructors.
55

66
use super::{abi, itron::task};
7-
use crate::cell::Cell;
8-
use crate::mem;
7+
use crate::cell::{Cell, RefCell};
98

109
#[thread_local]
1110
static REGISTERED: Cell<bool> = Cell::new(false);
1211

1312
#[thread_local]
14-
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
13+
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
1514

1615
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
1716
if !REGISTERED.get() {
@@ -22,18 +21,20 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
2221
REGISTERED.set(true);
2322
}
2423

25-
let list = unsafe { &mut DTORS };
26-
list.push((t, dtor));
24+
match DTORS.try_borrow_mut() {
25+
Ok(mut dtors) => dtors.push((t, dtor)),
26+
Err(_) => rtabort!("global allocator may not use TLS"),
27+
}
2728
}
2829

2930
pub unsafe fn run_dtors() {
30-
let mut list = mem::take(unsafe { &mut DTORS });
31+
let mut list = DTORS.take();
3132
while !list.is_empty() {
3233
for (ptr, dtor) in list {
3334
unsafe { dtor(ptr) };
3435
}
3536

36-
list = mem::take(unsafe { &mut DTORS });
37+
list = DTORS.take();
3738
}
3839
}
3940

library/std/src/sys/unix/thread_local_dtor.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,14 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
6969
// _tlv_atexit.
7070
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
7171
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
72-
use crate::cell::Cell;
73-
use crate::mem;
72+
use crate::cell::{Cell, RefCell};
7473
use crate::ptr;
7574

7675
#[thread_local]
7776
static REGISTERED: Cell<bool> = Cell::new(false);
7877

7978
#[thread_local]
80-
static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
79+
static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
8180

8281
if !REGISTERED.get() {
8382
_tlv_atexit(run_dtors, ptr::null_mut());
@@ -88,16 +87,18 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
8887
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
8988
}
9089

91-
let list = &mut DTORS;
92-
list.push((t, dtor));
90+
match DTORS.try_borrow_mut() {
91+
Ok(mut dtors) => dtors.push((t, dtor)),
92+
Err(_) => rtabort!("global allocator may not use TLS"),
93+
}
9394

9495
unsafe extern "C" fn run_dtors(_: *mut u8) {
95-
let mut list = mem::take(&mut DTORS);
96+
let mut list = DTORS.take();
9697
while !list.is_empty() {
9798
for (ptr, dtor) in list {
9899
dtor(ptr);
99100
}
100-
list = mem::take(&mut DTORS);
101+
list = DTORS.take();
101102
}
102103
}
103104
}

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

+15-4
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,19 @@ static HAS_DTORS: AtomicBool = AtomicBool::new(false);
1616
// Using a per-thread list avoids the problems in synchronizing global state.
1717
#[thread_local]
1818
#[cfg(target_thread_local)]
19-
static mut DESTRUCTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
19+
static DESTRUCTORS: crate::cell::RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> =
20+
crate::cell::RefCell::new(Vec::new());
2021

2122
// Ensure this can never be inlined because otherwise this may break in dylibs.
2223
// See #44391.
2324
#[inline(never)]
2425
#[cfg(target_thread_local)]
2526
pub unsafe fn register_keyless_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
26-
DESTRUCTORS.push((t, dtor));
27+
match DESTRUCTORS.try_borrow_mut() {
28+
Ok(mut dtors) => dtors.push((t, dtor)),
29+
Err(_) => rtabort!("global allocator may not use TLS"),
30+
}
31+
2732
HAS_DTORS.store(true, Relaxed);
2833
}
2934

@@ -37,11 +42,17 @@ unsafe fn run_keyless_dtors() {
3742
// the case that this loop always terminates because we provide the
3843
// guarantee that a TLS key cannot be set after it is flagged for
3944
// destruction.
40-
while let Some((ptr, dtor)) = DESTRUCTORS.pop() {
45+
loop {
46+
// Use a let-else binding to ensure the `RefCell` guard is dropped
47+
// immediately. Otherwise, a panic would occur if a TLS destructor
48+
// tries to access the list.
49+
let Some((ptr, dtor)) = DESTRUCTORS.borrow_mut().pop() else {
50+
break;
51+
};
4152
(dtor)(ptr);
4253
}
4354
// We're done so free the memory.
44-
DESTRUCTORS = Vec::new();
55+
DESTRUCTORS.replace(Vec::new());
4556
}
4657

4758
type Key = c::DWORD;

library/std/src/sys_common/thread_local_dtor.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#![unstable(feature = "thread_local_internals", issue = "none")]
1414
#![allow(dead_code)]
1515

16+
use crate::cell::RefCell;
1617
use crate::ptr;
1718
use crate::sys_common::thread_local_key::StaticKey;
1819

@@ -28,17 +29,23 @@ pub unsafe fn register_dtor_fallback(t: *mut u8, dtor: unsafe extern "C" fn(*mut
2829
// flagged for destruction.
2930

3031
static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
31-
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
32+
// FIXME(joboet): integrate RefCell into pointer to avoid infinite recursion
33+
// when the global allocator tries to register a destructor and just panic
34+
// instead.
35+
type List = RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>>;
3236
if DTORS.get().is_null() {
33-
let v: Box<List> = Box::new(Vec::new());
37+
let v: Box<List> = Box::new(RefCell::new(Vec::new()));
3438
DTORS.set(Box::into_raw(v) as *mut u8);
3539
}
36-
let list: &mut List = &mut *(DTORS.get() as *mut List);
37-
list.push((t, dtor));
40+
let list = &*(DTORS.get() as *const List);
41+
match list.try_borrow_mut() {
42+
Ok(mut dtors) => dtors.push((t, dtor)),
43+
Err(_) => rtabort!("global allocator may not use TLS"),
44+
}
3845

3946
unsafe extern "C" fn run_dtors(mut ptr: *mut u8) {
4047
while !ptr.is_null() {
41-
let list: Box<List> = Box::from_raw(ptr as *mut List);
48+
let list = Box::from_raw(ptr as *mut List).into_inner();
4249
for (ptr, dtor) in list.into_iter() {
4350
dtor(ptr);
4451
}

0 commit comments

Comments
 (0)