Skip to content

Commit f99566e

Browse files
committed
data_race: detect races between atomic and non-atomic accesses, even if both are reads
1 parent a4e42ad commit f99566e

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

src/tools/miri/src/concurrency/data_race.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ impl MemoryCellClocks {
374374
Ok(())
375375
}
376376

377-
/// Detect data-races with an atomic read, caused by a non-atomic write that does
377+
/// Detect data-races with an atomic read, caused by a non-atomic access that does
378378
/// not happen-before the atomic-read.
379379
fn atomic_read_detect(
380380
&mut self,
@@ -384,7 +384,12 @@ impl MemoryCellClocks {
384384
log::trace!("Atomic read with vectors: {:#?} :: {:#?}", self, thread_clocks);
385385
let atomic = self.atomic_mut();
386386
atomic.read_vector.set_at_index(&thread_clocks.clock, index);
387-
if self.write_was_before(&thread_clocks.clock) { Ok(()) } else { Err(DataRace) }
387+
// Make sure the last non-atomic write and all non-atomic reads were before this access.
388+
if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
389+
Ok(())
390+
} else {
391+
Err(DataRace)
392+
}
388393
}
389394

390395
/// Detect data-races with an atomic write, either with a non-atomic read or with
@@ -397,6 +402,7 @@ impl MemoryCellClocks {
397402
log::trace!("Atomic write with vectors: {:#?} :: {:#?}", self, thread_clocks);
398403
let atomic = self.atomic_mut();
399404
atomic.write_vector.set_at_index(&thread_clocks.clock, index);
405+
// Make sure the last non-atomic write and all non-atomic reads were before this access.
400406
if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
401407
Ok(())
402408
} else {
@@ -418,7 +424,10 @@ impl MemoryCellClocks {
418424
}
419425
if self.write_was_before(&thread_clocks.clock) {
420426
let race_free = if let Some(atomic) = self.atomic() {
427+
// We must be ordered-after all atomic accesses, reads and writes.
428+
// This ensures we don't mix atomic and non-atomic accesses.
421429
atomic.write_vector <= thread_clocks.clock
430+
&& atomic.read_vector <= thread_clocks.clock
422431
} else {
423432
true
424433
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@compile-flags: -Zmiri-preemption-rate=0.0
2+
use std::sync::atomic::{AtomicU16, Ordering};
3+
use std::thread;
4+
5+
// Make sure races between atomic and non-atomic reads are detected.
6+
// This seems harmless but C++ does not allow them, so we can't allow them for now either.
7+
// This test coverse the case where the non-atomic access come first.
8+
fn main() {
9+
let a = AtomicU16::new(0);
10+
11+
thread::scope(|s| {
12+
s.spawn(|| {
13+
let ptr = &a as *const AtomicU16 as *mut u16;
14+
unsafe { ptr.read() };
15+
});
16+
s.spawn(|| {
17+
thread::yield_now();
18+
a.load(Ordering::SeqCst);
19+
//~^ ERROR: Data race detected between (1) Read on thread `<unnamed>` and (2) Atomic Load on thread `<unnamed>`
20+
});
21+
});
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: Undefined Behavior: Data race detected between (1) Read on thread `<unnamed>` and (2) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
2+
--> $DIR/read_read_race1.rs:LL:CC
3+
|
4+
LL | a.load(Ordering::SeqCst);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ Data race detected between (1) Read on thread `<unnamed>` and (2) Atomic Load on thread `<unnamed>` at ALLOC. (2) just happened here
6+
|
7+
help: and (1) occurred earlier here
8+
--> $DIR/read_read_race1.rs:LL:CC
9+
|
10+
LL | unsafe { ptr.read() };
11+
| ^^^^^^^^^^
12+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
13+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
14+
= note: BACKTRACE (of the first span):
15+
= note: inside closure at $DIR/read_read_race1.rs:LL:CC
16+
17+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
18+
19+
error: aborting due to previous error
20+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//@compile-flags: -Zmiri-preemption-rate=0.0
2+
use std::sync::atomic::{AtomicU16, Ordering};
3+
use std::thread;
4+
5+
// Make sure races between atomic and non-atomic reads are detected.
6+
// This seems harmless but C++ does not allow them, so we can't allow them for now either.
7+
// This test coverse the case where the atomic access come first.
8+
fn main() {
9+
let a = AtomicU16::new(0);
10+
11+
thread::scope(|s| {
12+
s.spawn(|| {
13+
a.load(Ordering::SeqCst);
14+
});
15+
s.spawn(|| {
16+
thread::yield_now();
17+
let ptr = &a as *const AtomicU16 as *mut u16;
18+
unsafe { ptr.read() };
19+
//~^ ERROR: Data race detected between (1) Atomic Load on thread `<unnamed>` and (2) Read on thread `<unnamed>`
20+
});
21+
});
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: Undefined Behavior: Data race detected between (1) Atomic Load on thread `<unnamed>` and (2) Read on thread `<unnamed>` at ALLOC. (2) just happened here
2+
--> $DIR/read_read_race2.rs:LL:CC
3+
|
4+
LL | unsafe { ptr.read() };
5+
| ^^^^^^^^^^ Data race detected between (1) Atomic Load on thread `<unnamed>` and (2) Read on thread `<unnamed>` at ALLOC. (2) just happened here
6+
|
7+
help: and (1) occurred earlier here
8+
--> $DIR/read_read_race2.rs:LL:CC
9+
|
10+
LL | a.load(Ordering::SeqCst);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
13+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
14+
= note: BACKTRACE (of the first span):
15+
= note: inside closure at $DIR/read_read_race2.rs:LL:CC
16+
17+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
18+
19+
error: aborting due to previous error
20+

0 commit comments

Comments
 (0)