Open
Description
This does not affect stabilization of async fn
unless #[thread_local]
is also stabilized
#![feature(generators, generator_trait, thread_local)]
use std::ops::Generator;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
#[thread_local]
static FOO: AtomicUsize = AtomicUsize::new(0);
fn foo() -> impl Generator<Yield = (String, usize), Return = ()> {
static || {
let x = &FOO;
loop {
let s = format!("{:p}", x);
yield (s, x.fetch_add(1, Ordering::SeqCst));
}
}
}
fn main() {
unsafe {
let mut g = thread::spawn(|| {
let mut g = foo();
println!("&FOO = {:p}; resume() = {:?}", &FOO, g.resume());
g
}).join().unwrap();
println!("&FOO = {:p}; resume() = {:?}", &FOO, g.resume());
thread::spawn(move || {
println!("&FOO = {:p}; resume() = {:?}", &FOO, g.resume());
}).join().unwrap();
}
}
Sample output:
&FOO = 0x7f48f9bff688; resume() = Yielded(("0x7f48f9bff688", 0))
&FOO = 0x7f48fafd37c8; resume() = Yielded(("0x7f48f9bff688", 1))
&FOO = 0x7f48f9bff688; resume() = Yielded(("0x7f48f9bff688", 0))
You can see by the pointer addresses and values inside FOO
that the same location was reused for the second child thread (it's a bit harder to show a crash) - this is clearly an use-after-free.
If we had in-language async
, the same problem could be demonstrated using those.
In non-generator functions, such references have function-local lifetimes and cannot escape.
With the stable thread_local!
from libstd
, user code gets access to the reference in a (non-generator/async) closure, which also doesn't allow escaping the reference.
Metadata
Metadata
Assignees
Labels
Area: The borrow checkerArea: CoroutinesCategory: An issue proposing an enhancement or a PR with one.`#![feature(coroutines)]``#![feature(thread_local)]`Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessMedium priorityStatus: This bug is tracked inside the repo by a `known-bug` test.Relevant to the compiler team, which will review and decide on the PR/issue.This issue requires a nightly compiler in some way.
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
comex commentedon Apr 5, 2018
Am I right in assuming the same issue applies to implementations of thread locals in external crates, which aren't limited to unstable? e.g. https://amanieu.github.io/thread_local-rs/thread_local/index.html
edit: I'm wrong. From that page:
Otherwise the API would be blatantly unsound even without generators.
edit2: ignore me; I misunderstood the scope of the problem. For the record, it doesn't apply to
thread_local!
, or anything else that uses a callback.thread_local
stabilization #29594nikomatsakis commentedon Apr 12, 2018
triage: P-medium
Should fix, not urgent.
bstrie commentedon Feb 4, 2020
Now that async/await is stable, can anyone reproduce this bug using only those? I gave it a whack (playground here) and got the following error message:
...which suggests at least some degree of mitigation is already implemented, but it's possible that my translation (which obviously can't yield directly) did not represent the spirit of the original bug.
10 remaining items