rust-lang / rust-lang/rust

More global allocator reentrancy issues; this time `thread_local`

Open
#160,930 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-global-allocator-reentry A-thread C-bug I-unsound P-high T-libs
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Like #160837 and #160793


Edit: This issue affects a lot more code than I initially thought. Until we have a way to auto detect unknown/panicking/allocating code being reachable (these are all equivalent), it's probably for the best to keep it open.


GlobalAlloc guarantees that implementors can use thread_local for global allocators without being called reentrantly. Therefore, a panic from the implementation of TLS (thread local storage) in std violates that guarantee, as it results in a call to the global allocator. This is what I will explore in this issue.

Note that thread locals are generally lazy and can have destructors. Obviously noone expects those to somehow avoid the global allocator if they call the global allocator in the initializer or Drop impl. However, if the user specifies a thread local that does not involve the global allocator, then our guarantee better hold.


There are a number of different TLS implementations used in the standard library. For targets with platform support for TLS with destructors (cfg(target_thread_local)), we use native module, which defers to the #[thread_local] attr, which (IIUC) defers to LLVM. I did not find any global allocator calls in that code.

code that selects the TLS impl

https://github.com/rust-lang/rust/blob/a04c7a037d59b6dedc9f921e84e391f13a257aa4/library/std/src/sys/thread_local/mod.rs#L26-L48

For non-single-threaded platforms without such support (for example windows), the implementation lives in the sys::thread_local::os submodule. It implements TLS on top of some items provided for the platform: a Key type and some functions like unsafe fn set(key: Key, value: *mut u8). Note that due to laziness, both get and set can potentially be called from the global allocator.

get and set can cause panics on the following platforms:


unix

https://github.com/rust-lang/rust/blob/a04c7a037d59b6dedc9f921e84e391f13a257aa4/library/std/src/sys/thread_local/key/unix.rs#L35-L38
pthread_setspecific may return nonzero from memory exhaustion (docs).


windows

https://github.com/rust-lang/rust/blob/a04c7a037d59b6dedc9f921e84e391f13a257aa4/library/std/src/sys/thread_local/key/windows.rs#L140-L143
TlsSetValue may return zero "if it fails" (thank you microsoft)


xous

https://github.com/rust-lang/rust/blob/a04c7a037d59b6dedc9f921e84e391f13a257aa4/library/std/src/sys/thread_local/key/xous.rs#L136-L146
No idea if these can actually happen. Should still be aborts instead.


The other implementation, namely sgx, does it right by using rtassert/rtabort, which do not call the global allocator.

@rustbot label A-thread T-libs I-unsound A-global-allocator-reentry

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the TLS implementation selector in library/std/src/sys/thread_local/mod.rs, then trace the failure paths in key/unix.rs, key/windows.rs, and key/xous.rs. Compare them with the sgx implementation's rtassert/rtabort handling and the GlobalAlloc re-entrance guarantee. Done means the affected TLS paths handle failures without re-entering the global allocator, with platform-specific behavior verified.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.