Skip to content

Data race in thread::scope #98498

Closed
@RalfJung

Description

@RalfJung

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

Metadata

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