Skip to content

Keeping references to #[thread_local] statics is allowed across yields. #49682

Open
@eddyb

Description

@eddyb

This does not affect stabilization of async fn unless #[thread_local] is also stabilized

Try on playground:

#![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.

cc @alexcrichton @withoutboats @Zoxc

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-coroutinesArea: CoroutinesC-enhancementCategory: An issue proposing an enhancement or a PR with one.F-coroutines`#![feature(coroutines)]`F-thread_local`#![feature(thread_local)]`I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityS-bug-has-testStatus: This bug is tracked inside the repo by a `known-bug` test.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions