Skip to content

Data race in thread::scope #98498

Closed

Description

The following code sometimes causes a data race to be reported by Miri:

#![feature(atomic_from_mut, inline_const)]
use std::sync::atomic::{AtomicBool, Ordering};

fn main() {
let mut some_bools = [const { AtomicBool::new(false) }; 10];

let view: &mut [bool] = AtomicBool::get_mut_slice(&mut some_bools);
assert_eq!(view, [false; 10]);
view[..5].copy_from_slice(&[true; 5]);

std::thread::scope(|s| {
    for t in &some_bools[..5] {
        s.spawn(move || assert_eq!(t.load(Ordering::Relaxed), true));
    }

    for f in &some_bools[5..] {
        s.spawn(move || assert_eq!(f.load(Ordering::Relaxed), false));
    }
});
}

Miri reports:

note: tracking was triggered
   --> /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:133:17
    |
133 |       let scope = Scope {
    |  _________________^
134 | |         data: ScopeData {
135 | |             num_running_threads: AtomicUsize::new(0),
136 | |             main_thread: current(),
...   |
140 | |         scope: PhantomData,
141 | |     };
    | |_____^ created allocation with id 1082
    |
    = note: inside `std::thread::scope::<[closure@atomic.rs:12:20: 20:2], ()>` at /home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/std/src/thread/scoped.rs:133:17
note: inside `main` at atomic.rs:12:1
   --> atomic.rs:12:1
    |
12  | / std::thread::scope(|s| {
13  | |     for t in &some_bools[..5] {
14  | |         s.spawn(move || assert_eq!(t.load(Ordering::Relaxed), true));
15  | |     }
...   |
19  | |     }
20  | | });
    | |__^

error: Undefined Behavior: Data race detected between Deallocate on Thread(id = 0, name = "main") and Read on Thread(id = 2) at alloc1082+0x8 (current vector clock = VClock([114, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]), conflicting timestamp = VClock([113, 0, 9, 9, 9, 0, 0, 0, 0, 0, 9]))
  --> atomic.rs:12:1
   |
12 | / std::thread::scope(|s| {
13 | |     for t in &some_bools[..5] {
14 | |         s.spawn(move || assert_eq!(t.load(Ordering::Relaxed), true));
15 | |     }
...  |
19 | |     }
20 | | });
   | |__^ Data race detected between Deallocate on Thread(id = 0, name = "main") and Read on Thread(id = 2) at alloc1082+0x8 (current vector clock = VClock([114, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]), conflicting timestamp = VClock([113, 0, 9, 9, 9, 0, 0, 0, 0, 0, 9]))
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

   = note: inside `main` at atomic.rs:12:1

The deallocation occurs when scope returns and its local variable scope gets deallocated.

The read occurs here (I am fairly sure):

if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 {
self.main_thread.unpark();

This reads the contents of the field self.main_thread after the fetch_sub. But the moment the fetch_sub is done, the memory backing self might be deallocated. That makes the read a potential use-after-free, and a race with the deallocation.

Cc @m-ou-se

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions