4141//! on the data-race detection code.
4242
4343use std:: {
44+ borrow:: Cow ,
4445 cell:: { Cell , Ref , RefCell , RefMut } ,
4546 fmt:: Debug ,
4647 mem,
@@ -167,7 +168,7 @@ pub struct DataRace;
167168/// explicitly to reduce memory usage for the
168169/// common case where no atomic operations
169170/// exists on the memory cell.
170- #[ derive( Clone , PartialEq , Eq , Default , Debug ) ]
171+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
171172struct AtomicMemoryCellClocks {
172173 /// The clock-vector of the timestamp of the last atomic
173174 /// read operation performed by each thread.
@@ -186,6 +187,11 @@ struct AtomicMemoryCellClocks {
186187 /// happen-before a thread if an acquire-load is
187188 /// performed on the data.
188189 sync_vector : VClock ,
190+
191+ /// The size of accesses to this atomic location.
192+ /// We use this to detect non-synchronized mixed-size accesses. Since all accesses must be
193+ /// aligned to their size, this is sufficient to detect imperfectly overlapping accesses.
194+ size : Size ,
189195}
190196
191197/// Type of write operation: allocating memory
@@ -241,6 +247,17 @@ struct MemoryCellClocks {
241247 atomic_ops : Option < Box < AtomicMemoryCellClocks > > ,
242248}
243249
250+ impl AtomicMemoryCellClocks {
251+ fn new ( size : Size ) -> Self {
252+ AtomicMemoryCellClocks {
253+ read_vector : Default :: default ( ) ,
254+ write_vector : Default :: default ( ) ,
255+ sync_vector : Default :: default ( ) ,
256+ size,
257+ }
258+ }
259+ }
260+
244261impl MemoryCellClocks {
245262 /// Create a new set of clocks representing memory allocated
246263 /// at a given vector timestamp and index.
@@ -271,11 +288,39 @@ impl MemoryCellClocks {
271288 self . atomic_ops . as_deref ( )
272289 }
273290
274- /// Load or create the internal atomic memory metadata
275- /// if it does not exist.
291+ /// Load the internal atomic memory cells if they exist.
276292 #[ inline]
277- fn atomic_mut ( & mut self ) -> & mut AtomicMemoryCellClocks {
278- self . atomic_ops . get_or_insert_with ( Default :: default)
293+ fn atomic_mut_unwrap ( & mut self ) -> & mut AtomicMemoryCellClocks {
294+ self . atomic_ops . as_deref_mut ( ) . unwrap ( )
295+ }
296+
297+ /// Load or create the internal atomic memory metadata if it does not exist. Also ensures we do
298+ /// not do mixed-size atomic accesses, and updates the recorded atomic access size.
299+ fn atomic_access (
300+ & mut self ,
301+ thread_clocks : & ThreadClockSet ,
302+ size : Size ,
303+ ) -> Result < & mut AtomicMemoryCellClocks , DataRace > {
304+ match self . atomic_ops {
305+ Some ( ref mut atomic) => {
306+ // We are good if the size is the same or all atomic accesses are before our current time.
307+ if atomic. size == size {
308+ Ok ( atomic)
309+ } else if atomic. read_vector <= thread_clocks. clock
310+ && atomic. write_vector <= thread_clocks. clock
311+ {
312+ // This is now the new size that must be used for accesses here.
313+ atomic. size = size;
314+ Ok ( atomic)
315+ } else {
316+ Err ( DataRace )
317+ }
318+ }
319+ None => {
320+ self . atomic_ops = Some ( Box :: new ( AtomicMemoryCellClocks :: new ( size) ) ) ;
321+ Ok ( self . atomic_ops . as_mut ( ) . unwrap ( ) )
322+ }
323+ }
279324 }
280325
281326 /// Update memory cell data-race tracking for atomic
@@ -285,8 +330,9 @@ impl MemoryCellClocks {
285330 & mut self ,
286331 thread_clocks : & mut ThreadClockSet ,
287332 index : VectorIdx ,
333+ access_size : Size ,
288334 ) -> Result < ( ) , DataRace > {
289- self . atomic_read_detect ( thread_clocks, index) ?;
335+ self . atomic_read_detect ( thread_clocks, index, access_size ) ?;
290336 if let Some ( atomic) = self . atomic ( ) {
291337 thread_clocks. clock . join ( & atomic. sync_vector ) ;
292338 }
@@ -309,8 +355,9 @@ impl MemoryCellClocks {
309355 & mut self ,
310356 thread_clocks : & mut ThreadClockSet ,
311357 index : VectorIdx ,
358+ access_size : Size ,
312359 ) -> Result < ( ) , DataRace > {
313- self . atomic_read_detect ( thread_clocks, index) ?;
360+ self . atomic_read_detect ( thread_clocks, index, access_size ) ?;
314361 if let Some ( atomic) = self . atomic ( ) {
315362 thread_clocks. fence_acquire . join ( & atomic. sync_vector ) ;
316363 }
@@ -323,9 +370,10 @@ impl MemoryCellClocks {
323370 & mut self ,
324371 thread_clocks : & ThreadClockSet ,
325372 index : VectorIdx ,
373+ access_size : Size ,
326374 ) -> Result < ( ) , DataRace > {
327- self . atomic_write_detect ( thread_clocks, index) ?;
328- let atomic = self . atomic_mut ( ) ;
375+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
376+ let atomic = self . atomic_mut_unwrap ( ) ; // initialized by `atomic_write_detect`
329377 atomic. sync_vector . clone_from ( & thread_clocks. clock ) ;
330378 Ok ( ( ) )
331379 }
@@ -336,14 +384,15 @@ impl MemoryCellClocks {
336384 & mut self ,
337385 thread_clocks : & ThreadClockSet ,
338386 index : VectorIdx ,
387+ access_size : Size ,
339388 ) -> Result < ( ) , DataRace > {
340- self . atomic_write_detect ( thread_clocks, index) ?;
389+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
341390
342391 // The handling of release sequences was changed in C++20 and so
343392 // the code here is different to the paper since now all relaxed
344393 // stores block release sequences. The exception for same-thread
345394 // relaxed stores has been removed.
346- let atomic = self . atomic_mut ( ) ;
395+ let atomic = self . atomic_mut_unwrap ( ) ;
347396 atomic. sync_vector . clone_from ( & thread_clocks. fence_release ) ;
348397 Ok ( ( ) )
349398 }
@@ -354,9 +403,10 @@ impl MemoryCellClocks {
354403 & mut self ,
355404 thread_clocks : & ThreadClockSet ,
356405 index : VectorIdx ,
406+ access_size : Size ,
357407 ) -> Result < ( ) , DataRace > {
358- self . atomic_write_detect ( thread_clocks, index) ?;
359- let atomic = self . atomic_mut ( ) ;
408+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
409+ let atomic = self . atomic_mut_unwrap ( ) ;
360410 atomic. sync_vector . join ( & thread_clocks. clock ) ;
361411 Ok ( ( ) )
362412 }
@@ -367,9 +417,10 @@ impl MemoryCellClocks {
367417 & mut self ,
368418 thread_clocks : & ThreadClockSet ,
369419 index : VectorIdx ,
420+ access_size : Size ,
370421 ) -> Result < ( ) , DataRace > {
371- self . atomic_write_detect ( thread_clocks, index) ?;
372- let atomic = self . atomic_mut ( ) ;
422+ self . atomic_write_detect ( thread_clocks, index, access_size ) ?;
423+ let atomic = self . atomic_mut_unwrap ( ) ;
373424 atomic. sync_vector . join ( & thread_clocks. fence_release ) ;
374425 Ok ( ( ) )
375426 }
@@ -380,9 +431,10 @@ impl MemoryCellClocks {
380431 & mut self ,
381432 thread_clocks : & ThreadClockSet ,
382433 index : VectorIdx ,
434+ access_size : Size ,
383435 ) -> Result < ( ) , DataRace > {
384436 log:: trace!( "Atomic read with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
385- let atomic = self . atomic_mut ( ) ;
437+ let atomic = self . atomic_access ( thread_clocks , access_size ) ? ;
386438 atomic. read_vector . set_at_index ( & thread_clocks. clock , index) ;
387439 // Make sure the last non-atomic write and all non-atomic reads were before this access.
388440 if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
@@ -398,9 +450,10 @@ impl MemoryCellClocks {
398450 & mut self ,
399451 thread_clocks : & ThreadClockSet ,
400452 index : VectorIdx ,
453+ access_size : Size ,
401454 ) -> Result < ( ) , DataRace > {
402455 log:: trace!( "Atomic write with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
403- let atomic = self . atomic_mut ( ) ;
456+ let atomic = self . atomic_access ( thread_clocks , access_size ) ? ;
404457 atomic. write_vector . set_at_index ( & thread_clocks. clock , index) ;
405458 // Make sure the last non-atomic write and all non-atomic reads were before this access.
406459 if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
@@ -802,26 +855,44 @@ impl VClockAlloc {
802855 mem_clocks : & MemoryCellClocks ,
803856 action : & str ,
804857 is_atomic : bool ,
858+ access_size : Size ,
805859 ptr_dbg : Pointer < AllocId > ,
806860 ) -> InterpResult < ' tcx > {
807861 let ( current_index, current_clocks) = global. current_thread_state ( thread_mgr) ;
862+ let mut action = Cow :: Borrowed ( action) ;
808863 let write_clock;
809864 #[ rustfmt:: skip]
810865 let ( other_action, other_thread, other_clock) =
811866 if mem_clocks. write . 1 > current_clocks. clock [ mem_clocks. write . 0 ] {
812867 write_clock = mem_clocks. write ( ) ;
813- ( mem_clocks. write_type . get_descriptor ( ) , mem_clocks. write . 0 , & write_clock)
868+ ( mem_clocks. write_type . get_descriptor ( ) . to_owned ( ) , mem_clocks. write . 0 , & write_clock)
814869 } else if let Some ( idx) = Self :: find_gt_index ( & mem_clocks. read , & current_clocks. clock ) {
815- ( "Read" , idx, & mem_clocks. read )
870+ ( format ! ( "Read" ) , idx, & mem_clocks. read )
871+ } else if is_atomic && let Some ( atomic) = mem_clocks. atomic ( ) && atomic. size != access_size {
872+ // This is only a race if we are not synchronized with all atomic accesses, so find
873+ // the one we are not synchronized with.
874+ action = format ! ( "{}-byte (different-size) {action}" , access_size. bytes( ) ) . into ( ) ;
875+ if let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & current_clocks. clock )
876+ {
877+ ( format ! ( "{}-byte Atomic Store" , atomic. size. bytes( ) ) , idx, & atomic. write_vector )
878+ } else if let Some ( idx) =
879+ Self :: find_gt_index ( & atomic. read_vector , & current_clocks. clock )
880+ {
881+ ( format ! ( "{}-byte Atomic Load" , atomic. size. bytes( ) ) , idx, & atomic. read_vector )
882+ } else {
883+ unreachable ! (
884+ "Failed to report data-race for mixed-size access: no race found"
885+ )
886+ }
816887 } else if !is_atomic {
817888 if let Some ( atomic) = mem_clocks. atomic ( ) {
818889 if let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & current_clocks. clock )
819890 {
820- ( "Atomic Store" , idx, & atomic. write_vector )
891+ ( format ! ( "Atomic Store" ) , idx, & atomic. write_vector )
821892 } else if let Some ( idx) =
822893 Self :: find_gt_index ( & atomic. read_vector , & current_clocks. clock )
823894 {
824- ( "Atomic Load" , idx, & atomic. read_vector )
895+ ( format ! ( "Atomic Load" ) , idx, & atomic. read_vector )
825896 } else {
826897 unreachable ! (
827898 "Failed to report data-race for non-atomic operation: no race found"
@@ -905,7 +976,8 @@ impl VClockAlloc {
905976 & machine. threads ,
906977 mem_clocks,
907978 "Read" ,
908- false ,
979+ /* is_atomic */ false ,
980+ access_range. size ,
909981 Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
910982 ) ;
911983 }
@@ -944,7 +1016,8 @@ impl VClockAlloc {
9441016 & machine. threads ,
9451017 mem_clocks,
9461018 write_type. get_descriptor ( ) ,
947- false ,
1019+ /* is_atomic */ false ,
1020+ access_range. size ,
9481021 Pointer :: new ( alloc_id, Size :: from_bytes ( mem_clocks_range. start ) ) ,
9491022 ) ;
9501023 }
@@ -1072,9 +1145,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
10721145 "Atomic Load" ,
10731146 move |memory, clocks, index, atomic| {
10741147 if atomic == AtomicReadOrd :: Relaxed {
1075- memory. load_relaxed ( & mut * clocks, index)
1148+ memory. load_relaxed ( & mut * clocks, index, place . layout . size )
10761149 } else {
1077- memory. load_acquire ( & mut * clocks, index)
1150+ memory. load_acquire ( & mut * clocks, index, place . layout . size )
10781151 }
10791152 } ,
10801153 )
@@ -1095,9 +1168,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
10951168 "Atomic Store" ,
10961169 move |memory, clocks, index, atomic| {
10971170 if atomic == AtomicWriteOrd :: Relaxed {
1098- memory. store_relaxed ( clocks, index)
1171+ memory. store_relaxed ( clocks, index, place . layout . size )
10991172 } else {
1100- memory. store_release ( clocks, index)
1173+ memory. store_release ( clocks, index, place . layout . size )
11011174 }
11021175 } ,
11031176 )
@@ -1117,14 +1190,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11171190 this. validate_overlapping_atomic ( place) ?;
11181191 this. validate_atomic_op ( place, atomic, "Atomic RMW" , move |memory, clocks, index, _| {
11191192 if acquire {
1120- memory. load_acquire ( clocks, index) ?;
1193+ memory. load_acquire ( clocks, index, place . layout . size ) ?;
11211194 } else {
1122- memory. load_relaxed ( clocks, index) ?;
1195+ memory. load_relaxed ( clocks, index, place . layout . size ) ?;
11231196 }
11241197 if release {
1125- memory. rmw_release ( clocks, index)
1198+ memory. rmw_release ( clocks, index, place . layout . size )
11261199 } else {
1127- memory. rmw_relaxed ( clocks, index)
1200+ memory. rmw_relaxed ( clocks, index, place . layout . size )
11281201 }
11291202 } )
11301203 }
@@ -1175,7 +1248,8 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
11751248 & this. machine . threads ,
11761249 mem_clocks,
11771250 description,
1178- true ,
1251+ /* is_atomic */ true ,
1252+ place. layout . size ,
11791253 Pointer :: new (
11801254 alloc_id,
11811255 Size :: from_bytes ( mem_clocks_range. start ) ,
0 commit comments