Skip to content

Commit ddfbdd1

Browse files
committed
add main thread TLS data to static roots
1 parent 949b1dc commit ddfbdd1

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

src/shims/tls.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,21 @@ impl<'tcx> TlsData<'tcx> {
105105
thread_id: ThreadId,
106106
new_data: Scalar<Provenance>,
107107
cx: &impl HasDataLayout,
108+
static_roots: &mut Vec<AllocId>,
108109
) -> InterpResult<'tcx> {
109110
match self.keys.get_mut(&key) {
110111
Some(TlsEntry { data, .. }) => {
111112
if new_data.to_target_usize(cx)? != 0 {
112113
trace!("TLS key {} for thread {:?} stored: {:?}", key, thread_id, new_data);
113114
data.insert(thread_id, new_data);
115+
if thread_id.to_u32() == 0 {
116+
if let Some(alloc) = (new_data.to_pointer(cx).ok())
117+
.and_then(|ptr| ptr.provenance.and_then(|prov| prov.get_alloc_id()))
118+
{
119+
trace!("TLS key {} for main thread stored as static root", key);
120+
static_roots.push(alloc);
121+
}
122+
}
114123
} else {
115124
trace!("TLS key {} for thread {:?} removed", key, thread_id);
116125
data.remove(&thread_id);

src/shims/unix/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
305305
let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
306306
let active_thread = this.get_active_thread();
307307
let new_data = this.read_scalar(new_ptr)?;
308-
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
308+
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx, &mut this.machine.static_roots)?;
309309

310310
// Return success (`0`).
311311
this.write_null(dest)?;

src/shims/windows/foreign_items.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
260260
let key = u128::from(this.read_scalar(key)?.to_u32()?);
261261
let active_thread = this.get_active_thread();
262262
let new_data = this.read_scalar(new_ptr)?;
263-
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
263+
this.machine.tls.store_tls(
264+
key,
265+
active_thread,
266+
new_data,
267+
&*this.tcx,
268+
&mut this.machine.static_roots,
269+
)?;
264270

265271
// Return success (`1`).
266272
this.write_scalar(Scalar::from_i32(1), dest)?;

tests/fail/leak_in_tls.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@error-in-other-file: memory leaked
2+
//@normalize-stderr-test: ".*│.*" -> "$$stripped$$"
3+
4+
use std::cell::Cell;
5+
6+
pub fn main() {
7+
thread_local! {
8+
static REF: Cell<Option<&'static i32>> = Cell::new(None);
9+
}
10+
11+
std::thread::spawn(|| {
12+
REF.with(|cell| {
13+
let a = 123;
14+
let b = Box::new(a);
15+
let r = Box::leak(b);
16+
cell.set(Some(r));
17+
})
18+
})
19+
.join()
20+
.unwrap();
21+
22+
// Imagine the program running for a long time while the thread is gone
23+
// and this memory still sits around, unused -- leaked.
24+
}

tests/fail/leak_in_tls.stderr

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here:
2+
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
3+
|
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
8+
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC
9+
= note: inside `<std::alloc::Global as std::alloc::Allocator>::allocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
10+
= note: inside `alloc::alloc::exchange_malloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
11+
= note: inside `std::boxed::Box::<i32>::new` at RUSTLIB/alloc/src/boxed.rs:LL:CC
12+
note: inside closure
13+
--> $DIR/leak_in_tls.rs:LL:CC
14+
|
15+
LL | let b = Box::new(a);
16+
| ^^^^^^^^^^^
17+
= note: inside `std::thread::LocalKey::<std::cell::Cell<std::option::Option<&i32>>>::try_with::<[closure@$DIR/leak_in_tls.rs:LL:CC], ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC
18+
= note: inside `std::thread::LocalKey::<std::cell::Cell<std::option::Option<&i32>>>::with::<[closure@$DIR/leak_in_tls.rs:LL:CC], ()>` at RUSTLIB/std/src/thread/local.rs:LL:CC
19+
note: inside closure
20+
--> $DIR/leak_in_tls.rs:LL:CC
21+
|
22+
LL | / REF.with(|cell| {
23+
LL | | let a = 123;
24+
LL | | let b = Box::new(a);
25+
LL | | let r = Box::leak(b);
26+
LL | | cell.set(Some(r));
27+
LL | | })
28+
| |__________^
29+
30+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
31+
32+
note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
33+
34+
error: aborting due to previous error
35+

tests/pass/issues/issue-miri-2881.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use std::cell::Cell;
2+
3+
pub fn main() {
4+
let a = 123;
5+
let b = Box::new(a);
6+
let r = Box::leak(b);
7+
8+
thread_local! {
9+
static REF: Cell<Option<&'static i32>> = Cell::new(None);
10+
}
11+
12+
REF.with(|cell| {
13+
cell.set(Some(r));
14+
})
15+
}

0 commit comments

Comments
 (0)