Skip to content

Commit 62714a6

Browse files
committed
Fix C++20 SC access unsoundness
1 parent e2ed071 commit 62714a6

File tree

1 file changed

+46
-16
lines changed

1 file changed

+46
-16
lines changed

src/concurrency/weak_memory.rs

+46-16
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! but it is incapable of producing all possible weak behaviours allowed by the model. There are
77
//! certain weak behaviours observable on real hardware but not while using this.
88
//!
9-
//! Note that this implementation does not take into account of C++20's memory model revision to SC accesses
9+
//! Note that this implementation does not fully take into account of C++20's memory model revision to SC accesses
1010
//! and fences introduced by P0668 (<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0668r5.html>).
1111
//! This implementation is not fully correct under the revised C++20 model and may generate behaviours C++20
1212
//! disallows (<https://github.com/rust-lang/miri/issues/2301>).
@@ -134,9 +134,17 @@ struct StoreElement {
134134
// partially initialized data.
135135
val: ScalarMaybeUninit<Provenance>,
136136

137+
/// Metadata about loads from this store element,
138+
/// behind a RefCell to keep load op take &self
139+
load_info: RefCell<LoadInfo>,
140+
}
141+
142+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
143+
struct LoadInfo {
137144
/// Timestamp of first loads from this store element by each thread
138-
/// Behind a RefCell to keep load op take &self
139-
loads: RefCell<FxHashMap<VectorIdx, VTimestamp>>,
145+
timestamps: FxHashMap<VectorIdx, VTimestamp>,
146+
/// Whether this store element has been read by an SC load
147+
sc_loaded: bool,
140148
}
141149

142150
impl StoreBufferAlloc {
@@ -236,18 +244,23 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
236244
timestamp: 0,
237245
val: init,
238246
is_seqcst: false,
239-
loads: RefCell::new(FxHashMap::default()),
247+
load_info: RefCell::new(LoadInfo::default()),
240248
};
241249
ret.buffer.push_back(store_elem);
242250
ret
243251
}
244252

245253
/// Reads from the last store in modification order
246-
fn read_from_last_store(&self, global: &DataRaceState, thread_mgr: &ThreadManager<'_, '_>) {
254+
fn read_from_last_store(
255+
&self,
256+
global: &DataRaceState,
257+
thread_mgr: &ThreadManager<'_, '_>,
258+
is_seqcst: bool,
259+
) {
247260
let store_elem = self.buffer.back();
248261
if let Some(store_elem) = store_elem {
249262
let (index, clocks) = global.current_thread_state(thread_mgr);
250-
store_elem.load_impl(index, &clocks);
263+
store_elem.load_impl(index, &clocks, is_seqcst);
251264
}
252265
}
253266

@@ -277,7 +290,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
277290
validate()?;
278291

279292
let (index, clocks) = global.current_thread_state(thread_mgr);
280-
let loaded = store_elem.load_impl(index, &clocks);
293+
let loaded = store_elem.load_impl(index, &clocks, is_seqcst);
281294
Ok((loaded, recency))
282295
}
283296

@@ -322,9 +335,9 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
322335
// then we can't read-from anything earlier in modification order.
323336
// C++20 §6.9.2.2 [intro.races] paragraph 18
324337
false
325-
} else if store_elem.loads.borrow().iter().any(|(&load_index, &load_timestamp)| {
326-
load_timestamp <= clocks.clock[load_index]
327-
}) {
338+
} else if store_elem.load_info.borrow().timestamps.iter().any(
339+
|(&load_index, &load_timestamp)| load_timestamp <= clocks.clock[load_index],
340+
) {
328341
// CoRR: if there was a load from this store which happened-before the current load,
329342
// then we cannot read-from anything earlier in modification order.
330343
// C++20 §6.9.2.2 [intro.races] paragraph 16
@@ -341,12 +354,22 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
341354
// cannot read-before the last SC store executed before the fence.
342355
// C++17 §32.4 [atomics.order] paragraph 4
343356
false
344-
} else if is_seqcst && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index] {
357+
} else if is_seqcst
358+
&& store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index]
359+
{
345360
// The current SC load cannot read-before the last store sequenced-before
346361
// the last SC fence.
347362
// C++17 §32.4 [atomics.order] paragraph 5
348363
false
349-
} else {true};
364+
} else if is_seqcst && store_elem.load_info.borrow().sc_loaded {
365+
// The current SC load cannot read-before a store that an earlier SC load has observed.
366+
// See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427
367+
// Consequences of C++20 §31.4 [atomics.order] paragraph 3.1, 3.3 (coherence-ordered before)
368+
// and 4.1 (coherence-ordered before between SC makes global total order S)
369+
false
370+
} else {
371+
true
372+
};
350373

351374
true
352375
})
@@ -387,7 +410,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer {
387410
// access
388411
val,
389412
is_seqcst,
390-
loads: RefCell::new(FxHashMap::default()),
413+
load_info: RefCell::new(LoadInfo::default()),
391414
};
392415
self.buffer.push_back(store_elem);
393416
if self.buffer.len() > STORE_BUFFER_LIMIT {
@@ -419,8 +442,11 @@ impl StoreElement {
419442
&self,
420443
index: VectorIdx,
421444
clocks: &ThreadClockSet,
445+
is_seqcst: bool,
422446
) -> ScalarMaybeUninit<Provenance> {
423-
let _ = self.loads.borrow_mut().try_insert(index, clocks.clock[index]);
447+
let mut load_info = self.load_info.borrow_mut();
448+
load_info.sc_loaded |= is_seqcst;
449+
let _ = load_info.timestamps.try_insert(index, clocks.clock[index]);
424450
self.val
425451
}
426452
}
@@ -480,7 +506,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
480506
}
481507
let range = alloc_range(base_offset, place.layout.size);
482508
let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, init)?;
483-
buffer.read_from_last_store(global, threads);
509+
buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst);
484510
buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?;
485511
}
486512
Ok(())
@@ -587,7 +613,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
587613
if let Some(alloc_buffers) = this.get_alloc_extra(alloc_id)?.weak_memory.as_ref() {
588614
let buffer = alloc_buffers
589615
.get_or_create_store_buffer(alloc_range(base_offset, size), init)?;
590-
buffer.read_from_last_store(global, &this.machine.threads);
616+
buffer.read_from_last_store(
617+
global,
618+
&this.machine.threads,
619+
atomic == AtomicReadOrd::SeqCst,
620+
);
591621
}
592622
}
593623
Ok(())

0 commit comments

Comments
 (0)