mvdnes/spin-rs

Unsoundness in `Once::try_call_once()`

UnknownEclipse opened this issue · 0 comments

UB when try_call_once returns an error while multiple threads are attempting to access the Once. On debug builds this panics, but in release this falls back to unreachable() which is UB.

It looks like the cause is that the function expects that the state can only go from running->[complete, panicked] and doesn't take into account that it can go back to incomplete if the once function returns an error. The solution here would be retrying rather than hitting the unreachable.

Minimal Reproduce:

use std::{thread, time::Duration};

use spin::Once;

fn main() {
    let once = Once::new();

    thread::scope(|s| {
        s.spawn(|| {
            _ = once.try_call_once(|| {
                thread::sleep(Duration::from_secs(1));
                Err(())
            });
        });
        s.spawn(|| {
            thread::sleep(Duration::from_millis(10));
            once.call_once(|| {});
        });
    });
}