Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Treat thread-local statics on main thread as static roots for leakage analysis #2931

Merged
merged 1 commit into from Nov 12, 2023

Conversation

max-heller
Copy link
Contributor

Miri currently treats allocations as leaked if they're only referenced in thread-local statics. For threads other than the main thread, this is correct, since the thread can terminate before the program does, but references in the main thread's locals should be treated as living for the duration of the program since the thread lives for the duration of the program.

This PR adds thread-local statics and TLS keys as "static roots" for leakage analysis, but does not yet bless the example program from #2881. See #2881 (comment)

Closes #2881

@oli-obk
Copy link
Contributor

oli-obk commented Jun 20, 2023

I did some extra logging, and the issue turns out to be that all the static roots you added are deallocated at the end of the program, causing the leaked allocations within to not be detected anymore.

If you do not leak, then the thread local statics (even of the main thread) get freed. As an example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=3c4634970fddcc8784f4376a8e1241fd passes without leaking.

This means we cannot just apply what was proposed in #2881 (comment)

Knowing this, do you still think it's worth it to add this extra logic for something that goes away if the main thread TLS actually cleans up its memory?

@RalfJung
Copy link
Member

RalfJung commented Jun 25, 2023 via email

@RalfJung
Copy link
Member

@rustbot author

@rustbot rustbot added the S-waiting-on-author Status: Waiting for the PR author to address review comments label Jun 28, 2023
@max-heller
Copy link
Contributor Author

max-heller commented Jun 29, 2023

Knowing this, do you still think it's worth it to add this extra logic for something that goes away if the main thread TLS actually cleans up its memory?

Does this mean the value actually doesn't live for the lifetime of the program, or is it still valid to consider the program as ended before the main thread cleans up its TLS?

Oh right, we'd have to also skip deallocating main thread TLS.

Would this mean missing out on analysis of main thread TLS destructors? E.g. if a destructor contained UB we'd no longer be able to detect it?

Instead of skipping deallocation, could we run the leakage check before the main thread cleans up?

@rustbot label -S-waiting-on-author +S-waiting-on-review

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments labels Jun 29, 2023
@RalfJung
Copy link
Member

We should definitely run the destructor. We just shouldn't deallocate the backing storage.

pub fn main() {
thread_local! {
static REF: Cell<Option<&'static i32>> = Cell::new(None);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this will test that we allow leaks in one of the 2 kinds of TLS (TLS statics vs OS-supported TLS keys). We should make sure to have tests covering both (by directly using the underlying mechanisms).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added #[thread_local]s to the tests

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want one test that uses #[thread_local] and one that uses pthread TLS keys directly.

Or we only treat #[thread_local] as static roots? Then we cover all targets with target_thread_local. Doing special magic to library stuff like the pthread keys is somewhat dubious anyway...

@max-heller max-heller force-pushed the issue-2881 branch 3 times, most recently from 23ad36d to 81ea21b Compare July 8, 2023 15:22
@oli-obk
Copy link
Contributor

oli-obk commented Jul 12, 2023

looks like the msvc thread locals need a similar treatment. I think they have a completely separate code path

@max-heller
Copy link
Contributor Author

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: Waiting for the PR author to address review comments and removed S-waiting-on-review Status: Waiting for a review to complete labels Jul 17, 2023
@max-heller
Copy link
Contributor Author

looks like the msvc thread locals need a similar treatment. I think they have a completely separate code path

separate from this path, you mean?

// Thread-local storage
"TlsAlloc" => {
// This just creates a key; Windows does not natively support TLS destructors.
// Create key and return it.
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
}
"TlsGetValue" => {
let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.get_active_thread();
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"TlsSetValue" => {
let [key, new_ptr] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.get_active_thread();
let new_data = this.read_scalar(new_ptr)?;
this.machine.tls.store_tls(
key,
active_thread,
new_data,
&*this.tcx,
&mut this.machine.static_roots,
)?;

src/concurrency/thread.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

RalfJung commented Aug 4, 2023

Sorry for pointing you to shims/tls.rs, that was probably the wrong starting point. I think in a first step we just want to replace this here

miri/src/eval.rs

Lines 250 to 252 in 8bbc93c

// Need to call this ourselves since we are not going to return to the scheduler
// loop, and we want the main thread TLS to not show up as memory leaks.
this.terminate_active_thread()?;

by code that instead marks the thread_local statics of the main thread as "allowed to leak".
Then we should have tests:

  • for having some leaked memory in a #[thread_local] static.
  • for having some leaked memory in a tread_local! { static }. This test should ignore-windows since some Windows targets don't use #[thread_local] for their ``tread_local!`.

tests/fail/leak_in_tls.rs Outdated Show resolved Hide resolved
tests/fail/leak_in_tls.rs Outdated Show resolved Hide resolved
src/shims/tls.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

RalfJung commented Oct 5, 2023

@max-heller this is waiting for you to resolve the remaining comments. See the open threads above, in particular here and here.

Once this is ready for review again, please use @rustbot ready.

@max-heller
Copy link
Contributor Author

Addressed the open requests, sorry for the delay.
@rustbot ready

@rustbot rustbot removed the S-waiting-on-author Status: Waiting for the PR author to address review comments label Nov 10, 2023
@rustbot rustbot added the S-waiting-on-review Status: Waiting for a review to complete label Nov 10, 2023
@RalfJung
Copy link
Member

No worries, it's not like I had fast reaction times on this PR in the past. :)

src/eval.rs Outdated Show resolved Hide resolved
src/concurrency/thread.rs Outdated Show resolved Hide resolved
src/eval.rs Outdated Show resolved Hide resolved
@RalfJung

This comment was marked as outdated.

@RalfJung
Copy link
Member

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: Waiting for the PR author to address review comments and removed S-waiting-on-review Status: Waiting for a review to complete labels Nov 11, 2023
@max-heller
Copy link
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments labels Nov 11, 2023
src/concurrency/thread.rs Outdated Show resolved Hide resolved
src/concurrency/thread.rs Outdated Show resolved Hide resolved
@RalfJung
Copy link
Member

@rustbot author
I assume this is the last round, so please also squash this into a single commit.

@rustbot rustbot added S-waiting-on-author Status: Waiting for the PR author to address review comments and removed S-waiting-on-review Status: Waiting for a review to complete labels Nov 12, 2023
@max-heller
Copy link
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments labels Nov 12, 2023
@RalfJung
Copy link
Member

Awesome, thanks for staying with us until this is finally done. :)
@bors r+

@bors
Copy link
Collaborator

bors commented Nov 12, 2023

📌 Commit 2756f11 has been approved by RalfJung

It is now in the queue for this repository.

@bors
Copy link
Collaborator

bors commented Nov 12, 2023

⌛ Testing commit 2756f11 with merge 6db4c80...

@bors
Copy link
Collaborator

bors commented Nov 12, 2023

☀️ Test successful - checks-actions
Approved by: RalfJung
Pushing 6db4c80 to master...

1 similar comment
@bors
Copy link
Collaborator

bors commented Nov 12, 2023

☀️ Test successful - checks-actions
Approved by: RalfJung
Pushing 6db4c80 to master...

@bors bors merged commit 6db4c80 into rust-lang:master Nov 12, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Waiting for a review to complete
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Data reachable from thread-local storage of the main thread should not be considered leaked
5 participants