]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/src/concurrency/data_race.rs
Rollup merge of #104890 - lcnr:small-cleanup, r=fee1-dead
[rust.git] / src / tools / miri / src / concurrency / data_race.rs
1 //! Implementation of a data-race detector using Lamport Timestamps / Vector-clocks
2 //! based on the Dynamic Race Detection for C++:
3 //! <https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf>
4 //! which does not report false-positives when fences are used, and gives better
5 //! accuracy in presence of read-modify-write operations.
6 //!
7 //! The implementation contains modifications to correctly model the changes to the memory model in C++20
8 //! regarding the weakening of release sequences: <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0982r1.html>.
9 //! Relaxed stores now unconditionally block all currently active release sequences and so per-thread tracking of release
10 //! sequences is not needed.
11 //!
12 //! The implementation also models races with memory allocation and deallocation via treating allocation and
13 //! deallocation as a type of write internally for detecting data-races.
14 //!
15 //! Weak memory orders are explored but not all weak behaviours are exhibited, so it can still miss data-races
16 //! but should not report false-positives
17 //!
18 //! Data-race definition from(<https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races>):
19 //! a data race occurs between two memory accesses if they are on different threads, at least one operation
20 //! is non-atomic, at least one operation is a write and neither access happens-before the other. Read the link
21 //! for full definition.
22 //!
23 //! This re-uses vector indexes for threads that are known to be unable to report data-races, this is valid
24 //! because it only re-uses vector indexes once all currently-active (not-terminated) threads have an internal
25 //! vector clock that happens-after the join operation of the candidate thread. Threads that have not been joined
26 //! on are not considered. Since the thread's vector clock will only increase and a data-race implies that
27 //! there is some index x where `clock[x] > thread_clock`, when this is true `clock[candidate-idx] > thread_clock`
28 //! can never hold and hence a data-race can never be reported in that vector index again.
29 //! This means that the thread-index can be safely re-used, starting on the next timestamp for the newly created
30 //! thread.
31 //!
32 //! The timestamps used in the data-race detector assign each sequence of non-atomic operations
33 //! followed by a single atomic or concurrent operation a single timestamp.
34 //! Write, Read, Write, ThreadJoin will be represented by a single timestamp value on a thread.
35 //! This is because extra increment operations between the operations in the sequence are not
36 //! required for accurate reporting of data-race values.
37 //!
38 //! As per the paper a threads timestamp is only incremented after a release operation is performed
39 //! so some atomic operations that only perform acquires do not increment the timestamp. Due to shared
40 //! code some atomic operations may increment the timestamp when not necessary but this has no effect
41 //! on the data-race detection code.
42
43 use std::{
44     cell::{Cell, Ref, RefCell, RefMut},
45     fmt::Debug,
46     mem,
47 };
48
49 use rustc_ast::Mutability;
50 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
51 use rustc_index::vec::{Idx, IndexVec};
52 use rustc_middle::mir;
53 use rustc_target::abi::{Align, Size};
54
55 use crate::*;
56
57 use super::{
58     vector_clock::{VClock, VTimestamp, VectorIdx},
59     weak_memory::EvalContextExt as _,
60 };
61
62 pub type AllocExtra = VClockAlloc;
63
64 /// Valid atomic read-write orderings, alias of atomic::Ordering (not non-exhaustive).
65 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
66 pub enum AtomicRwOrd {
67     Relaxed,
68     Acquire,
69     Release,
70     AcqRel,
71     SeqCst,
72 }
73
74 /// Valid atomic read orderings, subset of atomic::Ordering.
75 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
76 pub enum AtomicReadOrd {
77     Relaxed,
78     Acquire,
79     SeqCst,
80 }
81
82 /// Valid atomic write orderings, subset of atomic::Ordering.
83 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
84 pub enum AtomicWriteOrd {
85     Relaxed,
86     Release,
87     SeqCst,
88 }
89
90 /// Valid atomic fence orderings, subset of atomic::Ordering.
91 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
92 pub enum AtomicFenceOrd {
93     Acquire,
94     Release,
95     AcqRel,
96     SeqCst,
97 }
98
99 /// The current set of vector clocks describing the state
100 /// of a thread, contains the happens-before clock and
101 /// additional metadata to model atomic fence operations.
102 #[derive(Clone, Default, Debug)]
103 pub(super) struct ThreadClockSet {
104     /// The increasing clock representing timestamps
105     /// that happen-before this thread.
106     pub(super) clock: VClock,
107
108     /// The set of timestamps that will happen-before this
109     /// thread once it performs an acquire fence.
110     fence_acquire: VClock,
111
112     /// The last timestamp of happens-before relations that
113     /// have been released by this thread by a fence.
114     fence_release: VClock,
115
116     /// Timestamps of the last SC fence performed by each
117     /// thread, updated when this thread performs an SC fence
118     pub(super) fence_seqcst: VClock,
119
120     /// Timestamps of the last SC write performed by each
121     /// thread, updated when this thread performs an SC fence
122     pub(super) write_seqcst: VClock,
123
124     /// Timestamps of the last SC fence performed by each
125     /// thread, updated when this thread performs an SC read
126     pub(super) read_seqcst: VClock,
127 }
128
129 impl ThreadClockSet {
130     /// Apply the effects of a release fence to this
131     /// set of thread vector clocks.
132     #[inline]
133     fn apply_release_fence(&mut self) {
134         self.fence_release.clone_from(&self.clock);
135     }
136
137     /// Apply the effects of an acquire fence to this
138     /// set of thread vector clocks.
139     #[inline]
140     fn apply_acquire_fence(&mut self) {
141         self.clock.join(&self.fence_acquire);
142     }
143
144     /// Increment the happens-before clock at a
145     /// known index.
146     #[inline]
147     fn increment_clock(&mut self, index: VectorIdx) {
148         self.clock.increment_index(index);
149     }
150
151     /// Join the happens-before clock with that of
152     /// another thread, used to model thread join
153     /// operations.
154     fn join_with(&mut self, other: &ThreadClockSet) {
155         self.clock.join(&other.clock);
156     }
157 }
158
159 /// Error returned by finding a data race
160 /// should be elaborated upon.
161 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
162 pub struct DataRace;
163
164 /// Externally stored memory cell clocks
165 /// explicitly to reduce memory usage for the
166 /// common case where no atomic operations
167 /// exists on the memory cell.
168 #[derive(Clone, PartialEq, Eq, Default, Debug)]
169 struct AtomicMemoryCellClocks {
170     /// The clock-vector of the timestamp of the last atomic
171     /// read operation performed by each thread.
172     /// This detects potential data-races between atomic read
173     /// and non-atomic write operations.
174     read_vector: VClock,
175
176     /// The clock-vector of the timestamp of the last atomic
177     /// write operation performed by each thread.
178     /// This detects potential data-races between atomic write
179     /// and non-atomic read or write operations.
180     write_vector: VClock,
181
182     /// Synchronization vector for acquire-release semantics
183     /// contains the vector of timestamps that will
184     /// happen-before a thread if an acquire-load is
185     /// performed on the data.
186     sync_vector: VClock,
187 }
188
189 /// Type of write operation: allocating memory
190 /// non-atomic writes and deallocating memory
191 /// are all treated as writes for the purpose
192 /// of the data-race detector.
193 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
194 enum WriteType {
195     /// Allocate memory.
196     Allocate,
197
198     /// Standard unsynchronized write.
199     Write,
200
201     /// Deallocate memory.
202     /// Note that when memory is deallocated first, later non-atomic accesses
203     /// will be reported as use-after-free, not as data races.
204     /// (Same for `Allocate` above.)
205     Deallocate,
206 }
207 impl WriteType {
208     fn get_descriptor(self) -> &'static str {
209         match self {
210             WriteType::Allocate => "Allocate",
211             WriteType::Write => "Write",
212             WriteType::Deallocate => "Deallocate",
213         }
214     }
215 }
216
217 /// Memory Cell vector clock metadata
218 /// for data-race detection.
219 #[derive(Clone, PartialEq, Eq, Debug)]
220 struct MemoryCellClocks {
221     /// The vector-clock timestamp of the last write
222     /// corresponding to the writing threads timestamp.
223     write: VTimestamp,
224
225     /// The identifier of the vector index, corresponding to a thread
226     /// that performed the last write operation.
227     write_index: VectorIdx,
228
229     /// The type of operation that the write index represents,
230     /// either newly allocated memory, a non-atomic write or
231     /// a deallocation of memory.
232     write_type: WriteType,
233
234     /// The vector-clock of the timestamp of the last read operation
235     /// performed by a thread since the last write operation occurred.
236     /// It is reset to zero on each write operation.
237     read: VClock,
238
239     /// Atomic acquire & release sequence tracking clocks.
240     /// For non-atomic memory in the common case this
241     /// value is set to None.
242     atomic_ops: Option<Box<AtomicMemoryCellClocks>>,
243 }
244
245 impl MemoryCellClocks {
246     /// Create a new set of clocks representing memory allocated
247     ///  at a given vector timestamp and index.
248     fn new(alloc: VTimestamp, alloc_index: VectorIdx) -> Self {
249         MemoryCellClocks {
250             read: VClock::default(),
251             write: alloc,
252             write_index: alloc_index,
253             write_type: WriteType::Allocate,
254             atomic_ops: None,
255         }
256     }
257
258     /// Load the internal atomic memory cells if they exist.
259     #[inline]
260     fn atomic(&self) -> Option<&AtomicMemoryCellClocks> {
261         self.atomic_ops.as_deref()
262     }
263
264     /// Load or create the internal atomic memory metadata
265     /// if it does not exist.
266     #[inline]
267     fn atomic_mut(&mut self) -> &mut AtomicMemoryCellClocks {
268         self.atomic_ops.get_or_insert_with(Default::default)
269     }
270
271     /// Update memory cell data-race tracking for atomic
272     /// load acquire semantics, is a no-op if this memory was
273     /// not used previously as atomic memory.
274     fn load_acquire(
275         &mut self,
276         clocks: &mut ThreadClockSet,
277         index: VectorIdx,
278     ) -> Result<(), DataRace> {
279         self.atomic_read_detect(clocks, index)?;
280         if let Some(atomic) = self.atomic() {
281             clocks.clock.join(&atomic.sync_vector);
282         }
283         Ok(())
284     }
285
286     /// Checks if the memory cell access is ordered with all prior atomic reads and writes
287     fn race_free_with_atomic(&self, clocks: &ThreadClockSet) -> bool {
288         if let Some(atomic) = self.atomic() {
289             atomic.read_vector <= clocks.clock && atomic.write_vector <= clocks.clock
290         } else {
291             true
292         }
293     }
294
295     /// Update memory cell data-race tracking for atomic
296     /// load relaxed semantics, is a no-op if this memory was
297     /// not used previously as atomic memory.
298     fn load_relaxed(
299         &mut self,
300         clocks: &mut ThreadClockSet,
301         index: VectorIdx,
302     ) -> Result<(), DataRace> {
303         self.atomic_read_detect(clocks, index)?;
304         if let Some(atomic) = self.atomic() {
305             clocks.fence_acquire.join(&atomic.sync_vector);
306         }
307         Ok(())
308     }
309
310     /// Update the memory cell data-race tracking for atomic
311     /// store release semantics.
312     fn store_release(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
313         self.atomic_write_detect(clocks, index)?;
314         let atomic = self.atomic_mut();
315         atomic.sync_vector.clone_from(&clocks.clock);
316         Ok(())
317     }
318
319     /// Update the memory cell data-race tracking for atomic
320     /// store relaxed semantics.
321     fn store_relaxed(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
322         self.atomic_write_detect(clocks, index)?;
323
324         // The handling of release sequences was changed in C++20 and so
325         // the code here is different to the paper since now all relaxed
326         // stores block release sequences. The exception for same-thread
327         // relaxed stores has been removed.
328         let atomic = self.atomic_mut();
329         atomic.sync_vector.clone_from(&clocks.fence_release);
330         Ok(())
331     }
332
333     /// Update the memory cell data-race tracking for atomic
334     /// store release semantics for RMW operations.
335     fn rmw_release(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
336         self.atomic_write_detect(clocks, index)?;
337         let atomic = self.atomic_mut();
338         atomic.sync_vector.join(&clocks.clock);
339         Ok(())
340     }
341
342     /// Update the memory cell data-race tracking for atomic
343     /// store relaxed semantics for RMW operations.
344     fn rmw_relaxed(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
345         self.atomic_write_detect(clocks, index)?;
346         let atomic = self.atomic_mut();
347         atomic.sync_vector.join(&clocks.fence_release);
348         Ok(())
349     }
350
351     /// Detect data-races with an atomic read, caused by a non-atomic write that does
352     /// not happen-before the atomic-read.
353     fn atomic_read_detect(
354         &mut self,
355         clocks: &ThreadClockSet,
356         index: VectorIdx,
357     ) -> Result<(), DataRace> {
358         log::trace!("Atomic read with vectors: {:#?} :: {:#?}", self, clocks);
359         if self.write <= clocks.clock[self.write_index] {
360             let atomic = self.atomic_mut();
361             atomic.read_vector.set_at_index(&clocks.clock, index);
362             Ok(())
363         } else {
364             Err(DataRace)
365         }
366     }
367
368     /// Detect data-races with an atomic write, either with a non-atomic read or with
369     /// a non-atomic write.
370     fn atomic_write_detect(
371         &mut self,
372         clocks: &ThreadClockSet,
373         index: VectorIdx,
374     ) -> Result<(), DataRace> {
375         log::trace!("Atomic write with vectors: {:#?} :: {:#?}", self, clocks);
376         if self.write <= clocks.clock[self.write_index] && self.read <= clocks.clock {
377             let atomic = self.atomic_mut();
378             atomic.write_vector.set_at_index(&clocks.clock, index);
379             Ok(())
380         } else {
381             Err(DataRace)
382         }
383     }
384
385     /// Detect races for non-atomic read operations at the current memory cell
386     /// returns true if a data-race is detected.
387     fn read_race_detect(
388         &mut self,
389         clocks: &ThreadClockSet,
390         index: VectorIdx,
391     ) -> Result<(), DataRace> {
392         log::trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, clocks);
393         if self.write <= clocks.clock[self.write_index] {
394             let race_free = if let Some(atomic) = self.atomic() {
395                 atomic.write_vector <= clocks.clock
396             } else {
397                 true
398             };
399             if race_free {
400                 self.read.set_at_index(&clocks.clock, index);
401                 Ok(())
402             } else {
403                 Err(DataRace)
404             }
405         } else {
406             Err(DataRace)
407         }
408     }
409
410     /// Detect races for non-atomic write operations at the current memory cell
411     /// returns true if a data-race is detected.
412     fn write_race_detect(
413         &mut self,
414         clocks: &ThreadClockSet,
415         index: VectorIdx,
416         write_type: WriteType,
417     ) -> Result<(), DataRace> {
418         log::trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, clocks);
419         if self.write <= clocks.clock[self.write_index] && self.read <= clocks.clock {
420             let race_free = if let Some(atomic) = self.atomic() {
421                 atomic.write_vector <= clocks.clock && atomic.read_vector <= clocks.clock
422             } else {
423                 true
424             };
425             if race_free {
426                 self.write = clocks.clock[index];
427                 self.write_index = index;
428                 self.write_type = write_type;
429                 self.read.set_zero_vector();
430                 Ok(())
431             } else {
432                 Err(DataRace)
433             }
434         } else {
435             Err(DataRace)
436         }
437     }
438 }
439
440 /// Evaluation context extensions.
441 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
442 pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
443     /// Perform an atomic read operation at the memory location.
444     fn read_scalar_atomic(
445         &self,
446         place: &MPlaceTy<'tcx, Provenance>,
447         atomic: AtomicReadOrd,
448     ) -> InterpResult<'tcx, Scalar<Provenance>> {
449         let this = self.eval_context_ref();
450         this.atomic_access_check(place)?;
451         // This will read from the last store in the modification order of this location. In case
452         // weak memory emulation is enabled, this may not be the store we will pick to actually read from and return.
453         // This is fine with StackedBorrow and race checks because they don't concern metadata on
454         // the *value* (including the associated provenance if this is an AtomicPtr) at this location.
455         // Only metadata on the location itself is used.
456         let scalar = this.allow_data_races_ref(move |this| this.read_scalar(&place.into()))?;
457         this.validate_overlapping_atomic(place)?;
458         this.buffered_atomic_read(place, atomic, scalar, || {
459             this.validate_atomic_load(place, atomic)
460         })
461     }
462
463     /// Perform an atomic write operation at the memory location.
464     fn write_scalar_atomic(
465         &mut self,
466         val: Scalar<Provenance>,
467         dest: &MPlaceTy<'tcx, Provenance>,
468         atomic: AtomicWriteOrd,
469     ) -> InterpResult<'tcx> {
470         let this = self.eval_context_mut();
471         this.atomic_access_check(dest)?;
472
473         this.validate_overlapping_atomic(dest)?;
474         this.allow_data_races_mut(move |this| this.write_scalar(val, &dest.into()))?;
475         this.validate_atomic_store(dest, atomic)?;
476         // FIXME: it's not possible to get the value before write_scalar. A read_scalar will cause
477         // side effects from a read the program did not perform. So we have to initialise
478         // the store buffer with the value currently being written
479         // ONCE this is fixed please remove the hack in buffered_atomic_write() in weak_memory.rs
480         // https://github.com/rust-lang/miri/issues/2164
481         this.buffered_atomic_write(val, dest, atomic, val)
482     }
483
484     /// Perform an atomic operation on a memory location.
485     fn atomic_op_immediate(
486         &mut self,
487         place: &MPlaceTy<'tcx, Provenance>,
488         rhs: &ImmTy<'tcx, Provenance>,
489         op: mir::BinOp,
490         neg: bool,
491         atomic: AtomicRwOrd,
492     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
493         let this = self.eval_context_mut();
494         this.atomic_access_check(place)?;
495
496         this.validate_overlapping_atomic(place)?;
497         let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
498
499         // Atomics wrap around on overflow.
500         let val = this.binary_op(op, &old, rhs)?;
501         let val = if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val };
502         this.allow_data_races_mut(|this| this.write_immediate(*val, &place.into()))?;
503
504         this.validate_atomic_rmw(place, atomic)?;
505
506         this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?;
507         Ok(old)
508     }
509
510     /// Perform an atomic exchange with a memory place and a new
511     /// scalar value, the old value is returned.
512     fn atomic_exchange_scalar(
513         &mut self,
514         place: &MPlaceTy<'tcx, Provenance>,
515         new: Scalar<Provenance>,
516         atomic: AtomicRwOrd,
517     ) -> InterpResult<'tcx, Scalar<Provenance>> {
518         let this = self.eval_context_mut();
519         this.atomic_access_check(place)?;
520
521         this.validate_overlapping_atomic(place)?;
522         let old = this.allow_data_races_mut(|this| this.read_scalar(&place.into()))?;
523         this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?;
524
525         this.validate_atomic_rmw(place, atomic)?;
526
527         this.buffered_atomic_rmw(new, place, atomic, old)?;
528         Ok(old)
529     }
530
531     /// Perform an conditional atomic exchange with a memory place and a new
532     /// scalar value, the old value is returned.
533     fn atomic_min_max_scalar(
534         &mut self,
535         place: &MPlaceTy<'tcx, Provenance>,
536         rhs: ImmTy<'tcx, Provenance>,
537         min: bool,
538         atomic: AtomicRwOrd,
539     ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
540         let this = self.eval_context_mut();
541         this.atomic_access_check(place)?;
542
543         this.validate_overlapping_atomic(place)?;
544         let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
545         let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
546
547         let new_val = if min {
548             if lt { &old } else { &rhs }
549         } else {
550             if lt { &rhs } else { &old }
551         };
552
553         this.allow_data_races_mut(|this| this.write_immediate(**new_val, &place.into()))?;
554
555         this.validate_atomic_rmw(place, atomic)?;
556
557         this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?;
558
559         // Return the old value.
560         Ok(old)
561     }
562
563     /// Perform an atomic compare and exchange at a given memory location.
564     /// On success an atomic RMW operation is performed and on failure
565     /// only an atomic read occurs. If `can_fail_spuriously` is true,
566     /// then we treat it as a "compare_exchange_weak" operation, and
567     /// some portion of the time fail even when the values are actually
568     /// identical.
569     fn atomic_compare_exchange_scalar(
570         &mut self,
571         place: &MPlaceTy<'tcx, Provenance>,
572         expect_old: &ImmTy<'tcx, Provenance>,
573         new: Scalar<Provenance>,
574         success: AtomicRwOrd,
575         fail: AtomicReadOrd,
576         can_fail_spuriously: bool,
577     ) -> InterpResult<'tcx, Immediate<Provenance>> {
578         use rand::Rng as _;
579         let this = self.eval_context_mut();
580         this.atomic_access_check(place)?;
581
582         this.validate_overlapping_atomic(place)?;
583         // Failure ordering cannot be stronger than success ordering, therefore first attempt
584         // to read with the failure ordering and if successful then try again with the success
585         // read ordering and write in the success case.
586         // Read as immediate for the sake of `binary_op()`
587         let old = this.allow_data_races_mut(|this| this.read_immediate(&(place.into())))?;
588         // `binary_op` will bail if either of them is not a scalar.
589         let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
590         // If the operation would succeed, but is "weak", fail some portion
591         // of the time, based on `success_rate`.
592         let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;
593         let cmpxchg_success = eq.to_scalar().to_bool()?
594             && if can_fail_spuriously {
595                 this.machine.rng.get_mut().gen_bool(success_rate)
596             } else {
597                 true
598             };
599         let res = Immediate::ScalarPair(old.to_scalar(), Scalar::from_bool(cmpxchg_success));
600
601         // Update ptr depending on comparison.
602         // if successful, perform a full rw-atomic validation
603         // otherwise treat this as an atomic load with the fail ordering.
604         if cmpxchg_success {
605             this.allow_data_races_mut(|this| this.write_scalar(new, &place.into()))?;
606             this.validate_atomic_rmw(place, success)?;
607             this.buffered_atomic_rmw(new, place, success, old.to_scalar())?;
608         } else {
609             this.validate_atomic_load(place, fail)?;
610             // A failed compare exchange is equivalent to a load, reading from the latest store
611             // in the modification order.
612             // Since `old` is only a value and not the store element, we need to separately
613             // find it in our store buffer and perform load_impl on it.
614             this.perform_read_on_buffered_latest(place, fail, old.to_scalar())?;
615         }
616
617         // Return the old value.
618         Ok(res)
619     }
620
621     /// Update the data-race detector for an atomic fence on the current thread.
622     fn atomic_fence(&mut self, atomic: AtomicFenceOrd) -> InterpResult<'tcx> {
623         let this = self.eval_context_mut();
624         if let Some(data_race) = &mut this.machine.data_race {
625             data_race.maybe_perform_sync_operation(&this.machine.threads, |index, mut clocks| {
626                 log::trace!("Atomic fence on {:?} with ordering {:?}", index, atomic);
627
628                 // Apply data-race detection for the current fences
629                 // this treats AcqRel and SeqCst as the same as an acquire
630                 // and release fence applied in the same timestamp.
631                 if atomic != AtomicFenceOrd::Release {
632                     // Either Acquire | AcqRel | SeqCst
633                     clocks.apply_acquire_fence();
634                 }
635                 if atomic != AtomicFenceOrd::Acquire {
636                     // Either Release | AcqRel | SeqCst
637                     clocks.apply_release_fence();
638                 }
639                 if atomic == AtomicFenceOrd::SeqCst {
640                     data_race.last_sc_fence.borrow_mut().set_at_index(&clocks.clock, index);
641                     clocks.fence_seqcst.join(&data_race.last_sc_fence.borrow());
642                     clocks.write_seqcst.join(&data_race.last_sc_write.borrow());
643                 }
644
645                 // Increment timestamp in case of release semantics.
646                 Ok(atomic != AtomicFenceOrd::Acquire)
647             })
648         } else {
649             Ok(())
650         }
651     }
652
653     /// After all threads are done running, this allows data races to occur for subsequent
654     /// 'administrative' machine accesses (that logically happen outside of the Abstract Machine).
655     fn allow_data_races_all_threads_done(&mut self) {
656         let this = self.eval_context_ref();
657         assert!(this.have_all_terminated());
658         if let Some(data_race) = &this.machine.data_race {
659             let old = data_race.ongoing_action_data_race_free.replace(true);
660             assert!(!old, "cannot nest allow_data_races");
661         }
662     }
663 }
664
665 /// Vector clock metadata for a logical memory allocation.
666 #[derive(Debug, Clone)]
667 pub struct VClockAlloc {
668     /// Assigning each byte a MemoryCellClocks.
669     alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
670 }
671
672 impl VisitTags for VClockAlloc {
673     fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
674         // No tags here.
675     }
676 }
677
678 impl VClockAlloc {
679     /// Create a new data-race detector for newly allocated memory.
680     pub fn new_allocation(
681         global: &GlobalState,
682         thread_mgr: &ThreadManager<'_, '_>,
683         len: Size,
684         kind: MemoryKind<MiriMemoryKind>,
685     ) -> VClockAlloc {
686         let (alloc_timestamp, alloc_index) = match kind {
687             // User allocated and stack memory should track allocation.
688             MemoryKind::Machine(
689                 MiriMemoryKind::Rust
690                 | MiriMemoryKind::Miri
691                 | MiriMemoryKind::C
692                 | MiriMemoryKind::WinHeap,
693             )
694             | MemoryKind::Stack => {
695                 let (alloc_index, clocks) = global.current_thread_state(thread_mgr);
696                 let alloc_timestamp = clocks.clock[alloc_index];
697                 (alloc_timestamp, alloc_index)
698             }
699             // Other global memory should trace races but be allocated at the 0 timestamp.
700             MemoryKind::Machine(
701                 MiriMemoryKind::Global
702                 | MiriMemoryKind::Machine
703                 | MiriMemoryKind::Runtime
704                 | MiriMemoryKind::ExternStatic
705                 | MiriMemoryKind::Tls,
706             )
707             | MemoryKind::CallerLocation => (0, VectorIdx::MAX_INDEX),
708         };
709         VClockAlloc {
710             alloc_ranges: RefCell::new(RangeMap::new(
711                 len,
712                 MemoryCellClocks::new(alloc_timestamp, alloc_index),
713             )),
714         }
715     }
716
717     // Find an index, if one exists where the value
718     // in `l` is greater than the value in `r`.
719     fn find_gt_index(l: &VClock, r: &VClock) -> Option<VectorIdx> {
720         log::trace!("Find index where not {:?} <= {:?}", l, r);
721         let l_slice = l.as_slice();
722         let r_slice = r.as_slice();
723         l_slice
724             .iter()
725             .zip(r_slice.iter())
726             .enumerate()
727             .find_map(|(idx, (&l, &r))| if l > r { Some(idx) } else { None })
728             .or_else(|| {
729                 if l_slice.len() > r_slice.len() {
730                     // By invariant, if l_slice is longer
731                     // then one element must be larger.
732                     // This just validates that this is true
733                     // and reports earlier elements first.
734                     let l_remainder_slice = &l_slice[r_slice.len()..];
735                     let idx = l_remainder_slice
736                         .iter()
737                         .enumerate()
738                         .find_map(|(idx, &r)| if r == 0 { None } else { Some(idx) })
739                         .expect("Invalid VClock Invariant");
740                     Some(idx + r_slice.len())
741                 } else {
742                     None
743                 }
744             })
745             .map(VectorIdx::new)
746     }
747
748     /// Report a data-race found in the program.
749     /// This finds the two racing threads and the type
750     /// of data-race that occurred. This will also
751     /// return info about the memory location the data-race
752     /// occurred in.
753     #[cold]
754     #[inline(never)]
755     fn report_data_race<'tcx>(
756         global: &GlobalState,
757         thread_mgr: &ThreadManager<'_, '_>,
758         range: &MemoryCellClocks,
759         action: &str,
760         is_atomic: bool,
761         ptr_dbg: Pointer<AllocId>,
762     ) -> InterpResult<'tcx> {
763         let (current_index, current_clocks) = global.current_thread_state(thread_mgr);
764         let write_clock;
765         let (other_action, other_thread, _other_clock) = if range.write
766             > current_clocks.clock[range.write_index]
767         {
768             // Convert the write action into the vector clock it
769             // represents for diagnostic purposes.
770             write_clock = VClock::new_with_index(range.write_index, range.write);
771             (range.write_type.get_descriptor(), range.write_index, &write_clock)
772         } else if let Some(idx) = Self::find_gt_index(&range.read, &current_clocks.clock) {
773             ("Read", idx, &range.read)
774         } else if !is_atomic {
775             if let Some(atomic) = range.atomic() {
776                 if let Some(idx) = Self::find_gt_index(&atomic.write_vector, &current_clocks.clock)
777                 {
778                     ("Atomic Store", idx, &atomic.write_vector)
779                 } else if let Some(idx) =
780                     Self::find_gt_index(&atomic.read_vector, &current_clocks.clock)
781                 {
782                     ("Atomic Load", idx, &atomic.read_vector)
783                 } else {
784                     unreachable!(
785                         "Failed to report data-race for non-atomic operation: no race found"
786                     )
787                 }
788             } else {
789                 unreachable!(
790                     "Failed to report data-race for non-atomic operation: no atomic component"
791                 )
792             }
793         } else {
794             unreachable!("Failed to report data-race for atomic operation")
795         };
796
797         // Load elaborated thread information about the racing thread actions.
798         let current_thread_info = global.print_thread_metadata(thread_mgr, current_index);
799         let other_thread_info = global.print_thread_metadata(thread_mgr, other_thread);
800
801         // Throw the data-race detection.
802         throw_ub_format!(
803             "Data race detected between {} on {} and {} on {} at {:?}",
804             action,
805             current_thread_info,
806             other_action,
807             other_thread_info,
808             ptr_dbg,
809         )
810     }
811
812     /// Detect racing atomic read and writes (not data races)
813     /// on every byte of the current access range
814     pub(super) fn race_free_with_atomic(
815         &self,
816         range: AllocRange,
817         global: &GlobalState,
818         thread_mgr: &ThreadManager<'_, '_>,
819     ) -> bool {
820         if global.race_detecting() {
821             let (_, clocks) = global.current_thread_state(thread_mgr);
822             let alloc_ranges = self.alloc_ranges.borrow();
823             for (_, range) in alloc_ranges.iter(range.start, range.size) {
824                 if !range.race_free_with_atomic(&clocks) {
825                     return false;
826                 }
827             }
828         }
829         true
830     }
831
832     /// Detect data-races for an unsynchronized read operation, will not perform
833     /// data-race detection if `race_detecting()` is false, either due to no threads
834     /// being created or if it is temporarily disabled during a racy read or write
835     /// operation for which data-race detection is handled separately, for example
836     /// atomic read operations.
837     pub fn read<'tcx>(
838         &self,
839         alloc_id: AllocId,
840         range: AllocRange,
841         machine: &MiriMachine<'_, '_>,
842     ) -> InterpResult<'tcx> {
843         let global = machine.data_race.as_ref().unwrap();
844         if global.race_detecting() {
845             let (index, clocks) = global.current_thread_state(&machine.threads);
846             let mut alloc_ranges = self.alloc_ranges.borrow_mut();
847             for (offset, range) in alloc_ranges.iter_mut(range.start, range.size) {
848                 if let Err(DataRace) = range.read_race_detect(&clocks, index) {
849                     // Report data-race.
850                     return Self::report_data_race(
851                         global,
852                         &machine.threads,
853                         range,
854                         "Read",
855                         false,
856                         Pointer::new(alloc_id, offset),
857                     );
858                 }
859             }
860             Ok(())
861         } else {
862             Ok(())
863         }
864     }
865
866     // Shared code for detecting data-races on unique access to a section of memory
867     fn unique_access<'tcx>(
868         &mut self,
869         alloc_id: AllocId,
870         range: AllocRange,
871         write_type: WriteType,
872         machine: &mut MiriMachine<'_, '_>,
873     ) -> InterpResult<'tcx> {
874         let global = machine.data_race.as_mut().unwrap();
875         if global.race_detecting() {
876             let (index, clocks) = global.current_thread_state(&machine.threads);
877             for (offset, range) in self.alloc_ranges.get_mut().iter_mut(range.start, range.size) {
878                 if let Err(DataRace) = range.write_race_detect(&clocks, index, write_type) {
879                     // Report data-race
880                     return Self::report_data_race(
881                         global,
882                         &machine.threads,
883                         range,
884                         write_type.get_descriptor(),
885                         false,
886                         Pointer::new(alloc_id, offset),
887                     );
888                 }
889             }
890             Ok(())
891         } else {
892             Ok(())
893         }
894     }
895
896     /// Detect data-races for an unsynchronized write operation, will not perform
897     /// data-race threads if `race_detecting()` is false, either due to no threads
898     /// being created or if it is temporarily disabled during a racy read or write
899     /// operation
900     pub fn write<'tcx>(
901         &mut self,
902         alloc_id: AllocId,
903         range: AllocRange,
904         machine: &mut MiriMachine<'_, '_>,
905     ) -> InterpResult<'tcx> {
906         self.unique_access(alloc_id, range, WriteType::Write, machine)
907     }
908
909     /// Detect data-races for an unsynchronized deallocate operation, will not perform
910     /// data-race threads if `race_detecting()` is false, either due to no threads
911     /// being created or if it is temporarily disabled during a racy read or write
912     /// operation
913     pub fn deallocate<'tcx>(
914         &mut self,
915         alloc_id: AllocId,
916         range: AllocRange,
917         machine: &mut MiriMachine<'_, '_>,
918     ) -> InterpResult<'tcx> {
919         self.unique_access(alloc_id, range, WriteType::Deallocate, machine)
920     }
921 }
922
923 impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {}
924 trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
925     /// Temporarily allow data-races to occur. This should only be used in
926     /// one of these cases:
927     /// - One of the appropriate `validate_atomic` functions will be called to
928     /// to treat a memory access as atomic.
929     /// - The memory being accessed should be treated as internal state, that
930     /// cannot be accessed by the interpreted program.
931     /// - Execution of the interpreted program execution has halted.
932     #[inline]
933     fn allow_data_races_ref<R>(&self, op: impl FnOnce(&MiriInterpCx<'mir, 'tcx>) -> R) -> R {
934         let this = self.eval_context_ref();
935         if let Some(data_race) = &this.machine.data_race {
936             let old = data_race.ongoing_action_data_race_free.replace(true);
937             assert!(!old, "cannot nest allow_data_races");
938         }
939         let result = op(this);
940         if let Some(data_race) = &this.machine.data_race {
941             data_race.ongoing_action_data_race_free.set(false);
942         }
943         result
944     }
945
946     /// Same as `allow_data_races_ref`, this temporarily disables any data-race detection and
947     /// so should only be used for atomic operations or internal state that the program cannot
948     /// access.
949     #[inline]
950     fn allow_data_races_mut<R>(
951         &mut self,
952         op: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> R,
953     ) -> R {
954         let this = self.eval_context_mut();
955         if let Some(data_race) = &this.machine.data_race {
956             let old = data_race.ongoing_action_data_race_free.replace(true);
957             assert!(!old, "cannot nest allow_data_races");
958         }
959         let result = op(this);
960         if let Some(data_race) = &this.machine.data_race {
961             data_race.ongoing_action_data_race_free.set(false);
962         }
963         result
964     }
965
966     /// Checks that an atomic access is legal at the given place.
967     fn atomic_access_check(&self, place: &MPlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
968         let this = self.eval_context_ref();
969         // Check alignment requirements. Atomics must always be aligned to their size,
970         // even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
971         // be 8-aligned).
972         let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
973         this.check_ptr_access_align(
974             place.ptr,
975             place.layout.size,
976             align,
977             CheckInAllocMsg::MemoryAccessTest,
978         )?;
979         // Ensure the allocation is mutable. Even failing (read-only) compare_exchange need mutable
980         // memory on many targets (i.e., they segfault if taht memory is mapped read-only), and
981         // atomic loads can be implemented via compare_exchange on some targets. There could
982         // possibly be some very specific exceptions to this, see
983         // <https://github.com/rust-lang/miri/pull/2464#discussion_r939636130> for details.
984         // We avoid `get_ptr_alloc` since we do *not* want to run the access hooks -- the actual
985         // access will happen later.
986         let (alloc_id, _offset, _prov) =
987             this.ptr_try_get_alloc_id(place.ptr).expect("there are no zero-sized atomic accesses");
988         if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
989             // FIXME: make this prettier, once these messages have separate title/span/help messages.
990             throw_ub_format!(
991                 "atomic operations cannot be performed on read-only memory\n\
992                 many platforms require atomic read-modify-write instructions to be performed on writeable memory, even if the operation fails \
993                 (and is hence nominally read-only)\n\
994                 some platforms implement (some) atomic loads via compare-exchange, which means they do not work on read-only memory; \
995                 it is possible that we could have an exception permitting this for specific kinds of loads\n\
996                 please report an issue at <https://github.com/rust-lang/miri/issues> if this is a problem for you"
997             );
998         }
999         Ok(())
1000     }
1001
1002     /// Update the data-race detector for an atomic read occurring at the
1003     /// associated memory-place and on the current thread.
1004     fn validate_atomic_load(
1005         &self,
1006         place: &MPlaceTy<'tcx, Provenance>,
1007         atomic: AtomicReadOrd,
1008     ) -> InterpResult<'tcx> {
1009         let this = self.eval_context_ref();
1010         this.validate_overlapping_atomic(place)?;
1011         this.validate_atomic_op(
1012             place,
1013             atomic,
1014             "Atomic Load",
1015             move |memory, clocks, index, atomic| {
1016                 if atomic == AtomicReadOrd::Relaxed {
1017                     memory.load_relaxed(&mut *clocks, index)
1018                 } else {
1019                     memory.load_acquire(&mut *clocks, index)
1020                 }
1021             },
1022         )
1023     }
1024
1025     /// Update the data-race detector for an atomic write occurring at the
1026     /// associated memory-place and on the current thread.
1027     fn validate_atomic_store(
1028         &mut self,
1029         place: &MPlaceTy<'tcx, Provenance>,
1030         atomic: AtomicWriteOrd,
1031     ) -> InterpResult<'tcx> {
1032         let this = self.eval_context_mut();
1033         this.validate_overlapping_atomic(place)?;
1034         this.validate_atomic_op(
1035             place,
1036             atomic,
1037             "Atomic Store",
1038             move |memory, clocks, index, atomic| {
1039                 if atomic == AtomicWriteOrd::Relaxed {
1040                     memory.store_relaxed(clocks, index)
1041                 } else {
1042                     memory.store_release(clocks, index)
1043                 }
1044             },
1045         )
1046     }
1047
1048     /// Update the data-race detector for an atomic read-modify-write occurring
1049     /// at the associated memory place and on the current thread.
1050     fn validate_atomic_rmw(
1051         &mut self,
1052         place: &MPlaceTy<'tcx, Provenance>,
1053         atomic: AtomicRwOrd,
1054     ) -> InterpResult<'tcx> {
1055         use AtomicRwOrd::*;
1056         let acquire = matches!(atomic, Acquire | AcqRel | SeqCst);
1057         let release = matches!(atomic, Release | AcqRel | SeqCst);
1058         let this = self.eval_context_mut();
1059         this.validate_overlapping_atomic(place)?;
1060         this.validate_atomic_op(place, atomic, "Atomic RMW", move |memory, clocks, index, _| {
1061             if acquire {
1062                 memory.load_acquire(clocks, index)?;
1063             } else {
1064                 memory.load_relaxed(clocks, index)?;
1065             }
1066             if release {
1067                 memory.rmw_release(clocks, index)
1068             } else {
1069                 memory.rmw_relaxed(clocks, index)
1070             }
1071         })
1072     }
1073
1074     /// Generic atomic operation implementation
1075     fn validate_atomic_op<A: Debug + Copy>(
1076         &self,
1077         place: &MPlaceTy<'tcx, Provenance>,
1078         atomic: A,
1079         description: &str,
1080         mut op: impl FnMut(
1081             &mut MemoryCellClocks,
1082             &mut ThreadClockSet,
1083             VectorIdx,
1084             A,
1085         ) -> Result<(), DataRace>,
1086     ) -> InterpResult<'tcx> {
1087         let this = self.eval_context_ref();
1088         if let Some(data_race) = &this.machine.data_race {
1089             if data_race.race_detecting() {
1090                 let size = place.layout.size;
1091                 let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr)?;
1092                 // Load and log the atomic operation.
1093                 // Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option.
1094                 let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap();
1095                 log::trace!(
1096                     "Atomic op({}) with ordering {:?} on {:?} (size={})",
1097                     description,
1098                     &atomic,
1099                     place.ptr,
1100                     size.bytes()
1101                 );
1102
1103                 // Perform the atomic operation.
1104                 data_race.maybe_perform_sync_operation(
1105                     &this.machine.threads,
1106                     |index, mut clocks| {
1107                         for (offset, range) in
1108                             alloc_meta.alloc_ranges.borrow_mut().iter_mut(base_offset, size)
1109                         {
1110                             if let Err(DataRace) = op(range, &mut clocks, index, atomic) {
1111                                 mem::drop(clocks);
1112                                 return VClockAlloc::report_data_race(
1113                                     data_race,
1114                                     &this.machine.threads,
1115                                     range,
1116                                     description,
1117                                     true,
1118                                     Pointer::new(alloc_id, offset),
1119                                 )
1120                                 .map(|_| true);
1121                             }
1122                         }
1123
1124                         // This conservatively assumes all operations have release semantics
1125                         Ok(true)
1126                     },
1127                 )?;
1128
1129                 // Log changes to atomic memory.
1130                 if log::log_enabled!(log::Level::Trace) {
1131                     for (_offset, range) in alloc_meta.alloc_ranges.borrow().iter(base_offset, size)
1132                     {
1133                         log::trace!(
1134                             "Updated atomic memory({:?}, size={}) to {:#?}",
1135                             place.ptr,
1136                             size.bytes(),
1137                             range.atomic_ops
1138                         );
1139                     }
1140                 }
1141             }
1142         }
1143         Ok(())
1144     }
1145 }
1146
1147 /// Extra metadata associated with a thread.
1148 #[derive(Debug, Clone, Default)]
1149 struct ThreadExtraState {
1150     /// The current vector index in use by the
1151     /// thread currently, this is set to None
1152     /// after the vector index has been re-used
1153     /// and hence the value will never need to be
1154     /// read during data-race reporting.
1155     vector_index: Option<VectorIdx>,
1156
1157     /// Thread termination vector clock, this
1158     /// is set on thread termination and is used
1159     /// for joining on threads since the vector_index
1160     /// may be re-used when the join operation occurs.
1161     termination_vector_clock: Option<VClock>,
1162 }
1163
1164 /// Global data-race detection state, contains the currently
1165 /// executing thread as well as the vector-clocks associated
1166 /// with each of the threads.
1167 // FIXME: it is probably better to have one large RefCell, than to have so many small ones.
1168 #[derive(Debug, Clone)]
1169 pub struct GlobalState {
1170     /// Set to true once the first additional
1171     /// thread has launched, due to the dependency
1172     /// between before and after a thread launch.
1173     /// Any data-races must be recorded after this
1174     /// so concurrent execution can ignore recording
1175     /// any data-races.
1176     multi_threaded: Cell<bool>,
1177
1178     /// A flag to mark we are currently performing
1179     /// a data race free action (such as atomic access)
1180     /// to supress the race detector
1181     ongoing_action_data_race_free: Cell<bool>,
1182
1183     /// Mapping of a vector index to a known set of thread
1184     /// clocks, this is not directly mapping from a thread id
1185     /// since it may refer to multiple threads.
1186     vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
1187
1188     /// Mapping of a given vector index to the current thread
1189     /// that the execution is representing, this may change
1190     /// if a vector index is re-assigned to a new thread.
1191     vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
1192
1193     /// The mapping of a given thread to associated thread metadata.
1194     thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
1195
1196     /// Potential vector indices that could be re-used on thread creation
1197     /// values are inserted here on after the thread has terminated and
1198     /// been joined with, and hence may potentially become free
1199     /// for use as the index for a new thread.
1200     /// Elements in this set may still require the vector index to
1201     /// report data-races, and can only be re-used after all
1202     /// active vector-clocks catch up with the threads timestamp.
1203     reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
1204
1205     /// This contains threads that have terminated, but not yet joined
1206     /// and so cannot become re-use candidates until a join operation
1207     /// occurs.
1208     /// The associated vector index will be moved into re-use candidates
1209     /// after the join operation occurs.
1210     terminated_threads: RefCell<FxHashMap<ThreadId, VectorIdx>>,
1211
1212     /// The timestamp of last SC fence performed by each thread
1213     last_sc_fence: RefCell<VClock>,
1214
1215     /// The timestamp of last SC write performed by each thread
1216     last_sc_write: RefCell<VClock>,
1217
1218     /// Track when an outdated (weak memory) load happens.
1219     pub track_outdated_loads: bool,
1220 }
1221
1222 impl VisitTags for GlobalState {
1223     fn visit_tags(&self, _visit: &mut dyn FnMut(SbTag)) {
1224         // We don't have any tags.
1225     }
1226 }
1227
1228 impl GlobalState {
1229     /// Create a new global state, setup with just thread-id=0
1230     /// advanced to timestamp = 1.
1231     pub fn new(config: &MiriConfig) -> Self {
1232         let mut global_state = GlobalState {
1233             multi_threaded: Cell::new(false),
1234             ongoing_action_data_race_free: Cell::new(false),
1235             vector_clocks: RefCell::new(IndexVec::new()),
1236             vector_info: RefCell::new(IndexVec::new()),
1237             thread_info: RefCell::new(IndexVec::new()),
1238             reuse_candidates: RefCell::new(FxHashSet::default()),
1239             terminated_threads: RefCell::new(FxHashMap::default()),
1240             last_sc_fence: RefCell::new(VClock::default()),
1241             last_sc_write: RefCell::new(VClock::default()),
1242             track_outdated_loads: config.track_outdated_loads,
1243         };
1244
1245         // Setup the main-thread since it is not explicitly created:
1246         // uses vector index and thread-id 0.
1247         let index = global_state.vector_clocks.get_mut().push(ThreadClockSet::default());
1248         global_state.vector_info.get_mut().push(ThreadId::new(0));
1249         global_state
1250             .thread_info
1251             .get_mut()
1252             .push(ThreadExtraState { vector_index: Some(index), termination_vector_clock: None });
1253
1254         global_state
1255     }
1256
1257     // We perform data race detection when there are more than 1 active thread
1258     // and we have not temporarily disabled race detection to perform something
1259     // data race free
1260     fn race_detecting(&self) -> bool {
1261         self.multi_threaded.get() && !self.ongoing_action_data_race_free.get()
1262     }
1263
1264     pub fn ongoing_action_data_race_free(&self) -> bool {
1265         self.ongoing_action_data_race_free.get()
1266     }
1267
1268     // Try to find vector index values that can potentially be re-used
1269     // by a new thread instead of a new vector index being created.
1270     fn find_vector_index_reuse_candidate(&self) -> Option<VectorIdx> {
1271         let mut reuse = self.reuse_candidates.borrow_mut();
1272         let vector_clocks = self.vector_clocks.borrow();
1273         let vector_info = self.vector_info.borrow();
1274         let terminated_threads = self.terminated_threads.borrow();
1275         for &candidate in reuse.iter() {
1276             let target_timestamp = vector_clocks[candidate].clock[candidate];
1277             if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| {
1278                 // The thread happens before the clock, and hence cannot report
1279                 // a data-race with this the candidate index.
1280                 let no_data_race = clock.clock[candidate] >= target_timestamp;
1281
1282                 // The vector represents a thread that has terminated and hence cannot
1283                 // report a data-race with the candidate index.
1284                 let thread_id = vector_info[clock_idx];
1285                 let vector_terminated =
1286                     reuse.contains(&clock_idx) || terminated_threads.contains_key(&thread_id);
1287
1288                 // The vector index cannot report a race with the candidate index
1289                 // and hence allows the candidate index to be re-used.
1290                 no_data_race || vector_terminated
1291             }) {
1292                 // All vector clocks for each vector index are equal to
1293                 // the target timestamp, and the thread is known to have
1294                 // terminated, therefore this vector clock index cannot
1295                 // report any more data-races.
1296                 assert!(reuse.remove(&candidate));
1297                 return Some(candidate);
1298             }
1299         }
1300         None
1301     }
1302
1303     // Hook for thread creation, enabled multi-threaded execution and marks
1304     // the current thread timestamp as happening-before the current thread.
1305     #[inline]
1306     pub fn thread_created(&mut self, thread_mgr: &ThreadManager<'_, '_>, thread: ThreadId) {
1307         let current_index = self.current_index(thread_mgr);
1308
1309         // Enable multi-threaded execution, there are now at least two threads
1310         // so data-races are now possible.
1311         self.multi_threaded.set(true);
1312
1313         // Load and setup the associated thread metadata
1314         let mut thread_info = self.thread_info.borrow_mut();
1315         thread_info.ensure_contains_elem(thread, Default::default);
1316
1317         // Assign a vector index for the thread, attempting to re-use an old
1318         // vector index that can no longer report any data-races if possible.
1319         let created_index = if let Some(reuse_index) = self.find_vector_index_reuse_candidate() {
1320             // Now re-configure the re-use candidate, increment the clock
1321             // for the new sync use of the vector.
1322             let vector_clocks = self.vector_clocks.get_mut();
1323             vector_clocks[reuse_index].increment_clock(reuse_index);
1324
1325             // Locate the old thread the vector was associated with and update
1326             // it to represent the new thread instead.
1327             let vector_info = self.vector_info.get_mut();
1328             let old_thread = vector_info[reuse_index];
1329             vector_info[reuse_index] = thread;
1330
1331             // Mark the thread the vector index was associated with as no longer
1332             // representing a thread index.
1333             thread_info[old_thread].vector_index = None;
1334
1335             reuse_index
1336         } else {
1337             // No vector re-use candidates available, instead create
1338             // a new vector index.
1339             let vector_info = self.vector_info.get_mut();
1340             vector_info.push(thread)
1341         };
1342
1343         log::trace!("Creating thread = {:?} with vector index = {:?}", thread, created_index);
1344
1345         // Mark the chosen vector index as in use by the thread.
1346         thread_info[thread].vector_index = Some(created_index);
1347
1348         // Create a thread clock set if applicable.
1349         let vector_clocks = self.vector_clocks.get_mut();
1350         if created_index == vector_clocks.next_index() {
1351             vector_clocks.push(ThreadClockSet::default());
1352         }
1353
1354         // Now load the two clocks and configure the initial state.
1355         let (current, created) = vector_clocks.pick2_mut(current_index, created_index);
1356
1357         // Join the created with current, since the current threads
1358         // previous actions happen-before the created thread.
1359         created.join_with(current);
1360
1361         // Advance both threads after the synchronized operation.
1362         // Both operations are considered to have release semantics.
1363         current.increment_clock(current_index);
1364         created.increment_clock(created_index);
1365     }
1366
1367     /// Hook on a thread join to update the implicit happens-before relation between the joined
1368     /// thread (the joinee, the thread that someone waited on) and the current thread (the joiner,
1369     /// the thread who was waiting).
1370     #[inline]
1371     pub fn thread_joined(
1372         &mut self,
1373         thread_mgr: &ThreadManager<'_, '_>,
1374         joiner: ThreadId,
1375         joinee: ThreadId,
1376     ) {
1377         let clocks_vec = self.vector_clocks.get_mut();
1378         let thread_info = self.thread_info.get_mut();
1379
1380         // Load the vector clock of the current thread.
1381         let current_index = thread_info[joiner]
1382             .vector_index
1383             .expect("Performed thread join on thread with no assigned vector");
1384         let current = &mut clocks_vec[current_index];
1385
1386         // Load the associated vector clock for the terminated thread.
1387         let join_clock = thread_info[joinee]
1388             .termination_vector_clock
1389             .as_ref()
1390             .expect("Joined with thread but thread has not terminated");
1391
1392         // The join thread happens-before the current thread
1393         // so update the current vector clock.
1394         // Is not a release operation so the clock is not incremented.
1395         current.clock.join(join_clock);
1396
1397         // Check the number of live threads, if the value is 1
1398         // then test for potentially disabling multi-threaded execution.
1399         if thread_mgr.get_live_thread_count() == 1 {
1400             // May potentially be able to disable multi-threaded execution.
1401             let current_clock = &clocks_vec[current_index];
1402             if clocks_vec
1403                 .iter_enumerated()
1404                 .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])
1405             {
1406                 // All thread terminations happen-before the current clock
1407                 // therefore no data-races can be reported until a new thread
1408                 // is created, so disable multi-threaded execution.
1409                 self.multi_threaded.set(false);
1410             }
1411         }
1412
1413         // If the thread is marked as terminated but not joined
1414         // then move the thread to the re-use set.
1415         let termination = self.terminated_threads.get_mut();
1416         if let Some(index) = termination.remove(&joinee) {
1417             let reuse = self.reuse_candidates.get_mut();
1418             reuse.insert(index);
1419         }
1420     }
1421
1422     /// On thread termination, the vector-clock may re-used
1423     /// in the future once all remaining thread-clocks catch
1424     /// up with the time index of the terminated thread.
1425     /// This assigns thread termination with a unique index
1426     /// which will be used to join the thread
1427     /// This should be called strictly before any calls to
1428     /// `thread_joined`.
1429     #[inline]
1430     pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_, '_>) {
1431         let current_index = self.current_index(thread_mgr);
1432
1433         // Increment the clock to a unique termination timestamp.
1434         let vector_clocks = self.vector_clocks.get_mut();
1435         let current_clocks = &mut vector_clocks[current_index];
1436         current_clocks.increment_clock(current_index);
1437
1438         // Load the current thread id for the executing vector.
1439         let vector_info = self.vector_info.get_mut();
1440         let current_thread = vector_info[current_index];
1441
1442         // Load the current thread metadata, and move to a terminated
1443         // vector state. Setting up the vector clock all join operations
1444         // will use.
1445         let thread_info = self.thread_info.get_mut();
1446         let current = &mut thread_info[current_thread];
1447         current.termination_vector_clock = Some(current_clocks.clock.clone());
1448
1449         // Add this thread as a candidate for re-use after a thread join
1450         // occurs.
1451         let termination = self.terminated_threads.get_mut();
1452         termination.insert(current_thread, current_index);
1453     }
1454
1455     /// Attempt to perform a synchronized operation, this
1456     /// will perform no operation if multi-threading is
1457     /// not currently enabled.
1458     /// Otherwise it will increment the clock for the current
1459     /// vector before and after the operation for data-race
1460     /// detection between any happens-before edges the
1461     /// operation may create.
1462     fn maybe_perform_sync_operation<'tcx>(
1463         &self,
1464         thread_mgr: &ThreadManager<'_, '_>,
1465         op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>,
1466     ) -> InterpResult<'tcx> {
1467         if self.multi_threaded.get() {
1468             let (index, clocks) = self.current_thread_state_mut(thread_mgr);
1469             if op(index, clocks)? {
1470                 let (_, mut clocks) = self.current_thread_state_mut(thread_mgr);
1471                 clocks.increment_clock(index);
1472             }
1473         }
1474         Ok(())
1475     }
1476
1477     /// Internal utility to identify a thread stored internally
1478     /// returns the id and the name for better diagnostics.
1479     fn print_thread_metadata(
1480         &self,
1481         thread_mgr: &ThreadManager<'_, '_>,
1482         vector: VectorIdx,
1483     ) -> String {
1484         let thread = self.vector_info.borrow()[vector];
1485         let thread_name = thread_mgr.get_thread_name(thread);
1486         format!("thread `{}`", String::from_utf8_lossy(thread_name))
1487     }
1488
1489     /// Acquire a lock, express that the previous call of
1490     /// `validate_lock_release` must happen before this.
1491     /// As this is an acquire operation, the thread timestamp is not
1492     /// incremented.
1493     pub fn validate_lock_acquire(&self, lock: &VClock, thread: ThreadId) {
1494         let (_, mut clocks) = self.load_thread_state_mut(thread);
1495         clocks.clock.join(lock);
1496     }
1497
1498     /// Release a lock handle, express that this happens-before
1499     /// any subsequent calls to `validate_lock_acquire`.
1500     /// For normal locks this should be equivalent to `validate_lock_release_shared`
1501     /// since an acquire operation should have occurred before, however
1502     /// for futex & condvar operations this is not the case and this
1503     /// operation must be used.
1504     pub fn validate_lock_release(&self, lock: &mut VClock, thread: ThreadId) {
1505         let (index, mut clocks) = self.load_thread_state_mut(thread);
1506         lock.clone_from(&clocks.clock);
1507         clocks.increment_clock(index);
1508     }
1509
1510     /// Release a lock handle, express that this happens-before
1511     /// any subsequent calls to `validate_lock_acquire` as well
1512     /// as any previous calls to this function after any
1513     /// `validate_lock_release` calls.
1514     /// For normal locks this should be equivalent to `validate_lock_release`.
1515     /// This function only exists for joining over the set of concurrent readers
1516     /// in a read-write lock and should not be used for anything else.
1517     pub fn validate_lock_release_shared(&self, lock: &mut VClock, thread: ThreadId) {
1518         let (index, mut clocks) = self.load_thread_state_mut(thread);
1519         lock.join(&clocks.clock);
1520         clocks.increment_clock(index);
1521     }
1522
1523     /// Load the vector index used by the given thread as well as the set of vector clocks
1524     /// used by the thread.
1525     #[inline]
1526     fn load_thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1527         let index = self.thread_info.borrow()[thread]
1528             .vector_index
1529             .expect("Loading thread state for thread with no assigned vector");
1530         let ref_vector = self.vector_clocks.borrow_mut();
1531         let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1532         (index, clocks)
1533     }
1534
1535     /// Load the current vector clock in use and the current set of thread clocks
1536     /// in use for the vector.
1537     #[inline]
1538     pub(super) fn current_thread_state(
1539         &self,
1540         thread_mgr: &ThreadManager<'_, '_>,
1541     ) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1542         let index = self.current_index(thread_mgr);
1543         let ref_vector = self.vector_clocks.borrow();
1544         let clocks = Ref::map(ref_vector, |vec| &vec[index]);
1545         (index, clocks)
1546     }
1547
1548     /// Load the current vector clock in use and the current set of thread clocks
1549     /// in use for the vector mutably for modification.
1550     #[inline]
1551     pub(super) fn current_thread_state_mut(
1552         &self,
1553         thread_mgr: &ThreadManager<'_, '_>,
1554     ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1555         let index = self.current_index(thread_mgr);
1556         let ref_vector = self.vector_clocks.borrow_mut();
1557         let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1558         (index, clocks)
1559     }
1560
1561     /// Return the current thread, should be the same
1562     /// as the data-race active thread.
1563     #[inline]
1564     fn current_index(&self, thread_mgr: &ThreadManager<'_, '_>) -> VectorIdx {
1565         let active_thread_id = thread_mgr.get_active_thread_id();
1566         self.thread_info.borrow()[active_thread_id]
1567             .vector_index
1568             .expect("active thread has no assigned vector")
1569     }
1570
1571     // SC ATOMIC STORE rule in the paper.
1572     pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_, '_>) {
1573         let (index, clocks) = self.current_thread_state(thread_mgr);
1574         self.last_sc_write.borrow_mut().set_at_index(&clocks.clock, index);
1575     }
1576
1577     // SC ATOMIC READ rule in the paper.
1578     pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_, '_>) {
1579         let (.., mut clocks) = self.current_thread_state_mut(thread_mgr);
1580         clocks.read_seqcst.join(&self.last_sc_fence.borrow());
1581     }
1582 }