1+ use std:: any:: Any ;
12use std:: cell:: RefCell ;
23use std:: collections:: VecDeque ;
34use std:: collections:: hash_map:: Entry ;
45use std:: default:: Default ;
56use std:: ops:: Not ;
67use std:: rc:: Rc ;
78use std:: time:: Duration ;
9+ use std:: { fmt, iter} ;
810
911use rustc_abi:: Size ;
1012use rustc_data_structures:: fx:: FxHashMap ;
1113
1214use super :: vector_clock:: VClock ;
1315use crate :: * ;
1416
17+ /// A trait for the synchronization metadata that can be attached to a memory location.
18+ pub trait SyncObj : Any {
19+ /// Determines whether this object's metadata shall be deleted when a write to its
20+ /// location occurs.
21+ fn delete_on_write ( & self ) -> bool {
22+ false
23+ }
24+ }
25+
26+ impl dyn SyncObj {
27+ #[ inline( always) ]
28+ pub fn downcast_ref < T : Any > ( & self ) -> Option < & T > {
29+ let x: & dyn Any = self ;
30+ x. downcast_ref ( )
31+ }
32+ }
33+
34+ impl fmt:: Debug for dyn SyncObj {
35+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
36+ f. debug_struct ( "SyncObj" ) . finish_non_exhaustive ( )
37+ }
38+ }
39+
1540/// The mutex state.
1641#[ derive( Default , Debug ) ]
1742struct Mutex {
@@ -214,15 +239,15 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
214239
215240impl < ' tcx > AllocExtra < ' tcx > {
216241 fn get_sync < T : ' static > ( & self , offset : Size ) -> Option < & T > {
217- self . sync . get ( & offset) . and_then ( |s| s. downcast_ref :: < T > ( ) )
242+ self . sync_objs . get ( & offset) . and_then ( |s| s. downcast_ref :: < T > ( ) )
218243 }
219244}
220245
221- /// We designate an `init`` field in all primitives .
222- /// If `init` is set to this, we consider the primitive initialized.
246+ /// We designate an `init`` field in all synchronization objects .
247+ /// If `init` is set to this, we consider the object initialized.
223248pub const LAZY_INIT_COOKIE : u32 = 0xcafe_affe ;
224249
225- // Public interface to synchronization primitives . Please note that in most
250+ // Public interface to synchronization objects . Please note that in most
226251// cases, the function calls are infallible and it is the client's (shim
227252// implementation's) responsibility to detect and deal with erroneous
228253// situations.
@@ -231,9 +256,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
231256 /// Helper for lazily initialized `alloc_extra.sync` data:
232257 /// this forces an immediate init.
233258 /// Return a reference to the data in the machine state.
234- fn lazy_sync_init < ' a , T : ' static > (
259+ fn lazy_sync_init < ' a , T : SyncObj > (
235260 & ' a mut self ,
236- primitive : & MPlaceTy < ' tcx > ,
261+ obj : & MPlaceTy < ' tcx > ,
237262 init_offset : Size ,
238263 data : T ,
239264 ) -> InterpResult < ' tcx , & ' a T >
@@ -242,27 +267,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
242267 {
243268 let this = self . eval_context_mut ( ) ;
244269
245- let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive. ptr ( ) , 0 ) ?;
246- let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
247- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
270+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( obj. ptr ( ) , 0 ) ?;
248271 // Mark this as "initialized".
249272 let init_cookie = Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ;
250- assert ! ( init_offset + init_cookie. size( ) <= primitive . layout. size) ;
251- let init_field = primitive . offset ( init_offset, this. machine . layouts . u32 , this) ?;
273+ assert ! ( init_offset + init_cookie. size( ) <= obj . layout. size) ;
274+ let init_field = obj . offset ( init_offset, this. machine . layouts . u32 , this) ?;
252275 this. write_scalar_atomic ( init_cookie, & init_field, AtomicWriteOrd :: Relaxed ) ?;
276+ // Insert sync obj, and return reference to it.
277+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
278+ alloc_extra. sync_objs . insert ( offset, Box :: new ( data) ) ;
253279 interp_ok ( this. get_alloc_extra ( alloc) ?. get_sync :: < T > ( offset) . unwrap ( ) )
254280 }
255281
256282 /// Helper for lazily initialized `alloc_extra.sync` data:
257- /// Checks if the primitive is initialized:
283+ /// Checks if the synchronization object is initialized:
258284 /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
259285 /// and stores that in `alloc_extra.sync`.
260- /// - Otherwise, calls `new_data` to initialize the primitive .
286+ /// - Otherwise, calls `new_data` to initialize the object .
261287 ///
262288 /// Return a reference to the data in the machine state.
263- fn lazy_sync_get_data < ' a , T : ' static > (
289+ fn lazy_sync_get_data < ' a , T : SyncObj > (
264290 & ' a mut self ,
265- primitive : & MPlaceTy < ' tcx > ,
291+ obj : & MPlaceTy < ' tcx > ,
266292 init_offset : Size ,
267293 missing_data : impl FnOnce ( ) -> InterpResult < ' tcx , T > ,
268294 new_data : impl FnOnce ( & mut MiriInterpCx < ' tcx > ) -> InterpResult < ' tcx , T > ,
@@ -276,8 +302,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
276302 // thread initializing. Needs to be an RMW operation to ensure we read the *latest* value.
277303 // So we just try to replace MUTEX_INIT_COOKIE with itself.
278304 let init_cookie = Scalar :: from_u32 ( LAZY_INIT_COOKIE ) ;
279- assert ! ( init_offset + init_cookie. size( ) <= primitive . layout. size) ;
280- let init_field = primitive . offset ( init_offset, this. machine . layouts . u32 , this) ?;
305+ assert ! ( init_offset + init_cookie. size( ) <= obj . layout. size) ;
306+ let init_field = obj . offset ( init_offset, this. machine . layouts . u32 , this) ?;
281307 let ( _init, success) = this
282308 . atomic_compare_exchange_scalar (
283309 & init_field,
@@ -290,27 +316,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290316 . to_scalar_pair ( ) ;
291317
292318 if success. to_bool ( ) ? {
293- // If it is initialized, it must be found in the "sync primitive " table,
319+ // If it is initialized, it must be found in the "sync obj " table,
294320 // or else it has been moved illegally.
295- let ( alloc, offset, _) = this. ptr_get_alloc_id ( primitive . ptr ( ) , 0 ) ?;
321+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( obj . ptr ( ) , 0 ) ?;
296322 let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
297323 // Due to borrow checker reasons, we have to do the lookup twice.
298324 if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
299325 let data = missing_data ( ) ?;
300- alloc_extra. sync . insert ( offset, Box :: new ( data) ) ;
326+ alloc_extra. sync_objs . insert ( offset, Box :: new ( data) ) ;
301327 }
302328 interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
303329 } else {
304330 let data = new_data ( this) ?;
305- this. lazy_sync_init ( primitive , init_offset, data)
331+ this. lazy_sync_init ( obj , init_offset, data)
306332 }
307333 }
308334
309- /// Get the synchronization primitive associated with the given pointer,
335+ /// Get the synchronization object associated with the given pointer,
310336 /// or initialize a new one.
311337 ///
312338 /// Return `None` if this pointer does not point to at least 1 byte of mutable memory.
313- fn get_sync_or_init < ' a , T : ' static > (
339+ fn get_sync_or_init < ' a , T : SyncObj > (
314340 & ' a mut self ,
315341 ptr : Pointer ,
316342 new : impl FnOnce ( & ' a mut MiriMachine < ' tcx > ) -> T ,
@@ -331,11 +357,94 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
331357 // Due to borrow checker reasons, we have to do the lookup twice.
332358 if alloc_extra. get_sync :: < T > ( offset) . is_none ( ) {
333359 let new = new ( machine) ;
334- alloc_extra. sync . insert ( offset, Box :: new ( new) ) ;
360+ alloc_extra. sync_objs . insert ( offset, Box :: new ( new) ) ;
335361 }
336362 Some ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
337363 }
338364
365+ /// Helper for "immovable" synchronization objects: the expected protocol for these objects is
366+ /// that they use a static initializer of `uninit_val`, and we set them to `init_val` upon
367+ /// initialization. At that point we also register a synchronization object, which is expected
368+ /// to have `delete_on_write() == true`. So in the future, if we still see the object, we know
369+ /// the location must still contain `init_val`. If the object is copied somewhere, that will
370+ /// show up as a non-`init_val` value without a synchronization object, which we can then use to
371+ /// error.
372+ ///
373+ /// `new_meta_obj` gets invoked when there is not yet an initialization object.
374+ /// It has to ensure that the in-memory representation indeed matches `uninit_val`.
375+ fn get_immovable_sync_with_static_init < ' a , T : SyncObj > (
376+ & ' a mut self ,
377+ obj : & MPlaceTy < ' tcx > ,
378+ init_offset : Size ,
379+ uninit_val : u8 ,
380+ init_val : u8 ,
381+ new_meta_obj : impl FnOnce ( & mut MiriInterpCx < ' tcx > ) -> InterpResult < ' tcx , T > ,
382+ ) -> InterpResult < ' tcx , & ' a T >
383+ where
384+ ' tcx : ' a ,
385+ {
386+ let this = self . eval_context_mut ( ) ;
387+ this. check_ptr_access ( obj. ptr ( ) , obj. layout . size , CheckInAllocMsg :: Dereferenceable ) ?;
388+ assert ! ( init_offset < obj. layout. size) ; // ensure our 1-byte flag fits
389+ let init_field = obj. offset ( init_offset, this. machine . layouts . u8 , this) ?;
390+
391+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( init_field. ptr ( ) , 0 ) ?;
392+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) ?;
393+ // Due to borrow checker reasons, we have to do the lookup twice.
394+ if alloc_extra. get_sync :: < T > ( offset) . is_some ( ) {
395+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) . unwrap ( ) ;
396+ return interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) ) ;
397+ }
398+
399+ // There's no sync object there yet. Create one, and try a CAS for uninit_val to init_val.
400+ let meta_obj = new_meta_obj ( this) ?;
401+ let ( _init, success) = this
402+ . atomic_compare_exchange_scalar (
403+ & init_field,
404+ & ImmTy :: from_scalar ( Scalar :: from_u8 ( uninit_val) , this. machine . layouts . u8 ) ,
405+ Scalar :: from_u8 ( init_val) ,
406+ AtomicRwOrd :: Relaxed ,
407+ AtomicReadOrd :: Relaxed ,
408+ /* can_fail_spuriously */ false ,
409+ ) ?
410+ . to_scalar_pair ( ) ;
411+ assert ! ( success. to_bool( ) ?, "`new_meta_obj` should have ensured that this CAS succeeds." ) ;
412+
413+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) . unwrap ( ) ;
414+ assert ! ( meta_obj. delete_on_write( ) ) ;
415+ alloc_extra. sync_objs . insert ( offset, Box :: new ( meta_obj) ) ;
416+ interp_ok ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) )
417+ }
418+
419+ /// Explicitly initializes an object that would usually be implicitly initialized with
420+ /// `get_immovable_sync_with_static_init`.
421+ fn init_immovable_sync < ' a , T : SyncObj > (
422+ & ' a mut self ,
423+ obj : & MPlaceTy < ' tcx > ,
424+ init_offset : Size ,
425+ init_val : u8 ,
426+ new_meta_obj : T ,
427+ ) -> InterpResult < ' tcx , Option < & ' a T > >
428+ where
429+ ' tcx : ' a ,
430+ {
431+ let this = self . eval_context_mut ( ) ;
432+ this. check_ptr_access ( obj. ptr ( ) , obj. layout . size , CheckInAllocMsg :: Dereferenceable ) ?;
433+ assert ! ( init_offset < obj. layout. size) ; // ensure our 1-byte flag fits
434+ let init_field = obj. offset ( init_offset, this. machine . layouts . u8 , this) ?;
435+
436+ // Zero the entire object, and then store `init_val` directly.
437+ this. write_bytes_ptr ( obj. ptr ( ) , iter:: repeat_n ( 0 , obj. layout . size . bytes_usize ( ) ) ) ?;
438+ this. write_scalar ( Scalar :: from_u8 ( init_val) , & init_field) ?;
439+
440+ // Create meta-level initialization object.
441+ let ( alloc, offset, _) = this. ptr_get_alloc_id ( init_field. ptr ( ) , 0 ) ?;
442+ let ( alloc_extra, _machine) = this. get_alloc_extra_mut ( alloc) . unwrap ( ) ;
443+ assert ! ( new_meta_obj. delete_on_write( ) ) ;
444+ alloc_extra. sync_objs . insert ( offset, Box :: new ( new_meta_obj) ) ;
445+ interp_ok ( Some ( alloc_extra. get_sync :: < T > ( offset) . unwrap ( ) ) )
446+ }
447+
339448 /// Lock by setting the mutex owner and increasing the lock count.
340449 fn mutex_lock ( & mut self , mutex_ref : & MutexRef ) -> InterpResult < ' tcx > {
341450 let this = self . eval_context_mut ( ) ;
0 commit comments