Skip to content

std::thread::local internals allow race conditions in safe but unstable code. #43733

Closed
@eddyb

Description

@eddyb

(try on playpen)

#![feature(const_fn, drop_types_in_const, thread_local_internals)]

type Foo = std::cell::RefCell<String>;

static __KEY: std::thread::__FastLocalKeyInner<Foo> =
    std::thread::__FastLocalKeyInner::new();
        
fn __getit() -> std::option::Option<
    &'static std::cell::UnsafeCell<
        std::option::Option<Foo>>>
{
    __KEY.get()
}

static FOO: std::thread::LocalKey<Foo> =
    std::thread::LocalKey::new(__getit, Default::default);

fn main() {
    FOO.with(|foo| println!("{}", foo.borrow()));
    std::thread::spawn(|| {
        FOO.with(|foo| *foo.borrow_mut() += "foo");
    }).join().unwrap();
    FOO.with(|foo| println!("{}", foo.borrow()));
}

Discovered while working on a sound fix for #17954. The problem is this decision which prevents the thread_local! macro from using unsafe to state that it actually has a #[thread_local] static to use in LocalKey. Without that, anything of 'static lifetime (which is btw quite wrong for #[thread_local]) and the right type can be used, from safe code.

While we can change the APIs all we want, going back to the correct solution of #[allow(unsafe_code)] could be a breaking change, without unsafety hygiene.

cc @rust-lang/libs

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessT-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions