Skip to content

Commit cd2edbf

Browse files
committed
ensure atomics happen on mutable allocations, and fix futex test
1 parent d630671 commit cd2edbf

File tree

6 files changed

+69
-3
lines changed

6 files changed

+69
-3
lines changed

src/concurrency/data_race.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use std::{
4646
mem,
4747
};
4848

49+
use rustc_ast::Mutability;
4950
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5051
use rustc_index::vec::{Idx, IndexVec};
5152
use rustc_middle::{mir, ty::layout::TyAndLayout};
@@ -476,6 +477,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
476477
align,
477478
CheckInAllocMsg::MemoryAccessTest,
478479
)?;
480+
// Ensure the allocation is mutable. Even failing (read-only) compare_exchange need mutable
481+
// memory on many targets (i.e., they segfault if taht memory is mapped read-only), and
482+
// atomic loads can be implemented via compare_exchange on some targets. See
483+
// <https://github.com/rust-lang/miri/issues/2463>.
484+
// We avoid `get_ptr_alloc` since we do *not* want to run the access hooks -- the actual
485+
// access will happen later.
486+
let (alloc_id, _offset, _prov) =
487+
this.ptr_try_get_alloc_id(place.ptr).expect("there are no zero-sized atomic accesses");
488+
if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
489+
throw_ub_format!("atomic operations cannot be performed on read-only memory");
490+
}
479491
Ok(())
480492
}
481493

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Should not rely on the aliasing model for its failure.
2+
//@compile-flags: -Zmiri-disable-stacked-borrows
3+
4+
use std::sync::atomic::{AtomicI32, Ordering};
5+
6+
fn main() {
7+
static X: i32 = 0;
8+
let x = &X as *const i32 as *const AtomicI32;
9+
let x = unsafe { &*x };
10+
x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed).unwrap_err(); //~ERROR: atomic operations cannot be performed on read-only memory
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: atomic operations cannot be performed on read-only memory
2+
--> $DIR/read_only_atomic_cmpxchg.rs:LL:CC
3+
|
4+
LL | x.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed).unwrap_err();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ atomic operations cannot be performed on read-only memory
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: backtrace:
10+
= note: inside `main` at $DIR/read_only_atomic_cmpxchg.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to previous error
15+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Should not rely on the aliasing model for its failure.
2+
//@compile-flags: -Zmiri-disable-stacked-borrows
3+
4+
use std::sync::atomic::{AtomicI32, Ordering};
5+
6+
fn main() {
7+
static X: i32 = 0;
8+
let x = &X as *const i32 as *const AtomicI32;
9+
let x = unsafe { &*x };
10+
// Some targets can implement atomic loads via compare_exchange, so we cannot allow them on
11+
// read-only memory.
12+
x.load(Ordering::Relaxed); //~ERROR: atomic operations cannot be performed on read-only memory
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: atomic operations cannot be performed on read-only memory
2+
--> $DIR/read_only_atomic_load.rs:LL:CC
3+
|
4+
LL | x.load(Ordering::Relaxed);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ atomic operations cannot be performed on read-only memory
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: backtrace:
10+
= note: inside `main` at $DIR/read_only_atomic_load.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to previous error
15+

tests/pass/concurrency/linux-futex.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fn wait_absolute_timeout() {
130130
fn wait_wake() {
131131
let start = Instant::now();
132132

133-
static FUTEX: i32 = 0;
133+
static mut FUTEX: i32 = 0;
134134

135135
let t = thread::spawn(move || {
136136
thread::sleep(Duration::from_millis(200));
@@ -167,7 +167,7 @@ fn wait_wake() {
167167
fn wait_wake_bitset() {
168168
let start = Instant::now();
169169

170-
static FUTEX: i32 = 0;
170+
static mut FUTEX: i32 = 0;
171171

172172
let t = thread::spawn(move || {
173173
thread::sleep(Duration::from_millis(200));
@@ -277,8 +277,8 @@ fn concurrent_wait_wake() {
277277

278278
// Make sure we got the interesting case (of having woken a thread) at least once, but not *each* time.
279279
let woken = WOKEN.load(Ordering::Relaxed);
280-
assert!(woken > 0 && woken < rounds);
281280
//eprintln!("waking happened {woken} times");
281+
assert!(woken > 0 && woken < rounds);
282282
}
283283

284284
fn main() {

0 commit comments

Comments
 (0)