Skip to content

std: Fix unsoundness of std::thread::ScopedKey #25952

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

Merged
merged 1 commit into from
Jun 16, 2015

Conversation

alexcrichton
Copy link
Member

Currently the compiler has no knowledge of #[thread_local] which forces users
to take on two burdens of unsafety:

  • The lifetime of the borrow of a #[thread_local] static is not 'static
  • Types in statics are required to be Sync

The thread-local modules mostly curb these facets of unsafety by only allowing
very limited scopes of borrows as well as allowing all types to be stored in a
thread-local key (regardless of whether they are Sync) through an unsafe impl.

Unfortunately these measures have the consequence of being able to take the
address of the key itself and send it to another thread, allowing the same key
to be accessed from two different threads. This is clearly unsafe, and this
commit fixes this problem with the same trick used by LocalKey, which is to
have an indirect function call to find the address of the current thread's
thread local. This way the address of thread local keys can safely be sent among
threads as their lifetime truly is 'static.

This commit will reduce the performance of cross-crate scoped thread locals as
it now requires an indirect function call, but this can likely be overcome in a
future commit.

Closes #25894

Currently the compiler has no knowledge of `#[thread_local]` which forces users
to take on two burdens of unsafety:

* The lifetime of the borrow of a `#[thread_local]` static is **not** `'static`
* Types in `static`s are required to be `Sync`

The thread-local modules mostly curb these facets of unsafety by only allowing
very limited scopes of borrows as well as allowing all types to be stored in a
thread-local key (regardless of whether they are `Sync`) through an `unsafe
impl`.

Unfortunately these measures have the consequence of being able to take the
address of the key itself and send it to another thread, allowing the same key
to be accessed from two different threads. This is clearly unsafe, and this
commit fixes this problem with the same trick used by `LocalKey`, which is to
have an indirect function call to find the address of the *current thread's*
thread local. This way the address of thread local keys can safely be sent among
threads as their lifetime truly is `'static`.

This commit will reduce the performance of cross-crate scoped thread locals as
it now requires an indirect function call, but this can likely be overcome in a
future commit.

Closes rust-lang#25894
@alexcrichton
Copy link
Member Author

r? @aturon

@rust-highfive rust-highfive assigned aturon and unassigned brson Jun 1, 2015
@rust-highfive
Copy link
Contributor

r? @brson

(rust_highfive has picked a reviewer for you, use r? to override)

@pythonesque
Copy link
Contributor

Not a fan of decreasing the performance of thread locals. At all.

"this can likely be overcome in a future commit" does not sound promising.

Hm, I suppose as long as this only affects the tls module and not the low level implementation I'm okay with it.

@alexcrichton
Copy link
Member Author

Not a fan of decreasing the performance of thread locals. At all.

To be clear, this only affects the performance of cross-crate scoped thread locals. This is an #[unstable] and currently unsound API, and fixing the soundness is absolutely paramount.

"this can likely be overcome in a future commit" does not sound promising.

By this I mean implementing a form of inlining for statics where the function accessor is inlined into external crates as, likely using available_externally linkage in LLVM. This is just not yet implemented, but is quite easy to do.

@alexcrichton alexcrichton added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Jun 2, 2015
@aturon
Copy link
Member

aturon commented Jun 16, 2015

Sorry for the delay...

The future of scoped thread locals is a little unclear; to the best of my knowledge they are barely used. So I think it's fine to take a perf hit for now, gaining soundness, and then continue to iterate on both the API and implementation over time. It's all unstable anyhow.

@bors: r+

@bors
Copy link
Collaborator

bors commented Jun 16, 2015

📌 Commit 4d3cffa has been approved by aturon

@bors
Copy link
Collaborator

bors commented Jun 16, 2015

⌛ Testing commit 4d3cffa with merge 3fdfef6...

@bors
Copy link
Collaborator

bors commented Jun 16, 2015

⛄ The build was interrupted to prioritize another pull request.

@bors
Copy link
Collaborator

bors commented Jun 16, 2015

⌛ Testing commit 4d3cffa with merge 467e4a6...

bors added a commit that referenced this pull request Jun 16, 2015
Currently the compiler has no knowledge of `#[thread_local]` which forces users
to take on two burdens of unsafety:

* The lifetime of the borrow of a `#[thread_local]` static is **not** `'static`
* Types in `static`s are required to be `Sync`

The thread-local modules mostly curb these facets of unsafety by only allowing
very limited scopes of borrows as well as allowing all types to be stored in a
thread-local key (regardless of whether they are `Sync`) through an `unsafe
impl`.

Unfortunately these measures have the consequence of being able to take the
address of the key itself and send it to another thread, allowing the same key
to be accessed from two different threads. This is clearly unsafe, and this
commit fixes this problem with the same trick used by `LocalKey`, which is to
have an indirect function call to find the address of the *current thread's*
thread local. This way the address of thread local keys can safely be sent among
threads as their lifetime truly is `'static`.

This commit will reduce the performance of cross-crate scoped thread locals as
it now requires an indirect function call, but this can likely be overcome in a
future commit.

Closes #25894
@bors bors merged commit 4d3cffa into rust-lang:master Jun 16, 2015
@alexcrichton alexcrichton deleted the fix-scoped-tls branch July 10, 2015 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

unsafe: ScopedKey allows for Sync-ification of non-Sync data
6 participants