@@ -125,81 +125,64 @@ pub struct NewPermission {
125125 /// Whether a read access should be performed on the non-frozen
126126 /// part on a retag.
127127 nonfreeze_access : bool ,
128+ /// Permission for memory outside the range.
129+ outside_perm : Permission ,
128130 /// Whether this pointer is part of the arguments of a function call.
129131 /// `protector` is `Some(_)` for all pointers marked `noalias`.
130132 protector : Option < ProtectorKind > ,
131133}
132134
133135impl < ' tcx > NewPermission {
134- /// Determine NewPermission of the reference from the type of the pointee.
135- fn from_ref_ty (
136+ /// Determine NewPermission of the reference/Box from the type of the pointee.
137+ ///
138+ /// A `ref_mutability` of `None` indicates a `Box` type.
139+ fn new (
136140 pointee : Ty < ' tcx > ,
137- mutability : Mutability ,
138- kind : RetagKind ,
141+ ref_mutability : Option < Mutability > ,
142+ retag_kind : RetagKind ,
139143 cx : & crate :: MiriInterpCx < ' tcx > ,
140144 ) -> Option < Self > {
141145 let ty_is_unpin = pointee. is_unpin ( * cx. tcx , cx. typing_env ( ) ) ;
142- let is_protected = kind == RetagKind :: FnEntry ;
143- let protector = is_protected. then_some ( ProtectorKind :: StrongProtector ) ;
144-
145- Some ( match mutability {
146- Mutability :: Mut if ty_is_unpin =>
147- NewPermission {
148- freeze_perm : Permission :: new_reserved (
149- /* ty_is_freeze */ true ,
150- is_protected,
151- ) ,
152- freeze_access : true ,
153- nonfreeze_perm : Permission :: new_reserved (
154- /* ty_is_freeze */ false ,
155- is_protected,
156- ) ,
157- // If we have a mutable reference, then the non-frozen part will
158- // have state `ReservedIM` or `Reserved`, which can have an initial read access
159- // performed on it because you cannot have multiple mutable borrows.
160- nonfreeze_access : true ,
161- protector,
162- } ,
163- Mutability :: Not =>
164- NewPermission {
165- freeze_perm : Permission :: new_frozen ( ) ,
166- freeze_access : true ,
167- nonfreeze_perm : Permission :: new_cell ( ) ,
168- // If it is a shared reference, then the non-frozen
169- // part will have state `Cell`, which should not have an initial access,
170- // as this can cause data races when using thread-safe data types like
171- // `Mutex<T>`.
172- nonfreeze_access : false ,
173- protector,
174- } ,
175- _ => return None ,
176- } )
177- }
146+ let ty_is_freeze = pointee. is_freeze ( * cx. tcx , cx. typing_env ( ) ) ;
147+ let is_protected = retag_kind == RetagKind :: FnEntry ;
178148
179- /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
180- /// These pointers allow deallocation so need a different kind of protector not handled
181- /// by `from_ref_ty`.
182- fn from_unique_ty (
183- ty : Ty < ' tcx > ,
184- kind : RetagKind ,
185- cx : & crate :: MiriInterpCx < ' tcx > ,
186- ) -> Option < Self > {
187- let pointee = ty. builtin_deref ( true ) . unwrap ( ) ;
188- pointee. is_unpin ( * cx. tcx , cx. typing_env ( ) ) . then_some ( ( ) ) . map ( |( ) | {
189- // Regular `Unpin` box, give it `noalias` but only a weak protector
190- // because it is valid to deallocate it within the function.
191- let is_protected = kind == RetagKind :: FnEntry ;
192- let protector = is_protected. then_some ( ProtectorKind :: WeakProtector ) ;
193- NewPermission {
194- freeze_perm : Permission :: new_reserved ( /* ty_is_freeze */ true , is_protected) ,
195- freeze_access : true ,
196- nonfreeze_perm : Permission :: new_reserved (
197- /* ty_is_freeze */ false ,
198- is_protected,
199- ) ,
200- nonfreeze_access : true ,
201- protector,
202- }
149+ if matches ! ( ref_mutability, Some ( Mutability :: Mut ) | None if !ty_is_unpin) {
150+ // Mutable reference / Box to pinning type: retagging is a NOP.
151+ // FIXME: with `UnsafePinned`, this should do proper per-byte tracking.
152+ return None ;
153+ }
154+
155+ let freeze_perm = match ref_mutability {
156+ // Shared references are frozen.
157+ Some ( Mutability :: Not ) => Permission :: new_frozen ( ) ,
158+ // Mutable references and Boxes are reserved.
159+ _ => Permission :: new_reserved_frz ( ) ,
160+ } ;
161+ let nonfreeze_perm = match ref_mutability {
162+ // Shared references are "transparent".
163+ Some ( Mutability :: Not ) => Permission :: new_cell ( ) ,
164+ // *Protected* mutable references and boxes are reserved without regarding for interior mutability.
165+ _ if is_protected => Permission :: new_reserved_frz ( ) ,
166+ // Unprotected mutable references and boxes start in `ReservedIm`.
167+ _ => Permission :: new_reserved_im ( ) ,
168+ } ;
169+
170+ // Everything except for `Cell` gets an initial access.
171+ let initial_access = |perm : & Permission | !perm. is_cell ( ) ;
172+
173+ Some ( NewPermission {
174+ freeze_perm,
175+ freeze_access : initial_access ( & freeze_perm) ,
176+ nonfreeze_perm,
177+ nonfreeze_access : initial_access ( & nonfreeze_perm) ,
178+ outside_perm : if ty_is_freeze { freeze_perm } else { nonfreeze_perm } ,
179+ protector : is_protected. then_some ( if ref_mutability. is_some ( ) {
180+ // Strong protector for references
181+ ProtectorKind :: StrongProtector
182+ } else {
183+ // Weak protector for boxes
184+ ProtectorKind :: WeakProtector
185+ } ) ,
203186 } )
204187 }
205188}
@@ -313,16 +296,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
313296
314297 let span = this. machine . current_span ( ) ;
315298
316- // Store initial permissions and their corresponding range.
317- let mut perms_map: DedupRangeMap < LocationState > = DedupRangeMap :: new (
318- ptr_size,
319- LocationState :: new_accessed ( Permission :: new_disabled ( ) , IdempotentForeignAccess :: None ) , // this will be overwritten
320- ) ;
321- // Keep track of whether the node has any part that allows for interior mutability.
322- // FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker
323- // for requesting interior mutability.
324- let mut has_unsafe_cell = false ;
325-
326299 // When adding a new node, the SIFA of its parents needs to be updated, potentially across
327300 // the entire memory range. For the parts that are being accessed below, the access itself
328301 // trivially takes care of that. However, we have to do some more work to also deal with
@@ -350,66 +323,48 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
350323 . get_tree_borrows_params ( )
351324 . precise_interior_mut ;
352325
353- let default_perm = if !precise_interior_mut {
354- // NOTE: Using `ty_is_freeze` doesn't give the same result as going through the range
355- // and computing `has_unsafe_cell`. This is because of zero-sized `UnsafeCell`, for which
356- // `has_unsafe_cell` is false, but `!ty_is_freeze` is true.
357- let ty_is_freeze = place. layout . ty . is_freeze ( * this. tcx , this. typing_env ( ) ) ;
358- let ( perm, access) = if ty_is_freeze {
326+ // Compute initial "inside" permissions.
327+ let loc_state = |frozen : bool | -> LocationState {
328+ let ( perm, access) = if frozen {
359329 ( new_perm. freeze_perm , new_perm. freeze_access )
360330 } else {
361331 ( new_perm. nonfreeze_perm , new_perm. nonfreeze_access )
362332 } ;
363333 let sifa = perm. strongest_idempotent_foreign_access ( protected) ;
364- let new_loc = if access {
334+ if access {
365335 LocationState :: new_accessed ( perm, sifa)
366336 } else {
367337 LocationState :: new_non_accessed ( perm, sifa)
368- } ;
369-
370- for ( _loc_range, loc) in perms_map. iter_mut_all ( ) {
371- * loc = new_loc;
372338 }
373-
374- perm
339+ } ;
340+ let perms_map = if !precise_interior_mut {
341+ // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`.
342+ let ty_is_freeze = place. layout . ty . is_freeze ( * this. tcx , this. typing_env ( ) ) ;
343+ let state = loc_state ( ty_is_freeze) ;
344+ DedupRangeMap :: new ( ptr_size, state)
375345 } else {
346+ // The initial state will be overwritten by the visitor below.
347+ let mut perms_map: DedupRangeMap < LocationState > = DedupRangeMap :: new (
348+ ptr_size,
349+ LocationState :: new_accessed (
350+ Permission :: new_disabled ( ) ,
351+ IdempotentForeignAccess :: None ,
352+ ) ,
353+ ) ;
376354 this. visit_freeze_sensitive ( place, ptr_size, |range, frozen| {
377- has_unsafe_cell = has_unsafe_cell || !frozen;
378-
379- // We are only ever `Frozen` inside the frozen bits.
380- let ( perm, access) = if frozen {
381- ( new_perm. freeze_perm , new_perm. freeze_access )
382- } else {
383- ( new_perm. nonfreeze_perm , new_perm. nonfreeze_access )
384- } ;
385- let sifa = perm. strongest_idempotent_foreign_access ( protected) ;
386- // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if`
387- // doesn't not change whether any code is UB or not. We could just always use
388- // `new_accessed` and everything would stay the same. But that seems conceptually
389- // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether
390- // a read access is performed below.
391- let new_loc = if access {
392- LocationState :: new_accessed ( perm, sifa)
393- } else {
394- LocationState :: new_non_accessed ( perm, sifa)
395- } ;
396-
397- // Store initial permissions.
355+ let state = loc_state ( frozen) ;
398356 for ( _loc_range, loc) in perms_map. iter_mut ( range. start , range. size ) {
399- * loc = new_loc ;
357+ * loc = state ;
400358 }
401-
402359 interp_ok ( ( ) )
403360 } ) ?;
404-
405- // Allow lazily writing to surrounding data if we found an `UnsafeCell`.
406- if has_unsafe_cell { new_perm. nonfreeze_perm } else { new_perm. freeze_perm }
361+ perms_map
407362 } ;
408363
409364 let alloc_extra = this. get_alloc_extra ( alloc_id) ?;
410365 let mut tree_borrows = alloc_extra. borrow_tracker_tb ( ) . borrow_mut ( ) ;
411366
412- for ( perm_range, perm) in perms_map. iter_mut_all ( ) {
367+ for ( perm_range, perm) in perms_map. iter_all ( ) {
413368 if perm. is_accessed ( ) {
414369 // Some reborrows incur a read access to the parent.
415370 // Adjust range to be relative to allocation start (rather than to `place`).
@@ -447,7 +402,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
447402 orig_tag,
448403 new_tag,
449404 perms_map,
450- default_perm ,
405+ new_perm . outside_perm ,
451406 protected,
452407 span,
453408 ) ?;
@@ -514,7 +469,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
514469 let this = self . eval_context_mut ( ) ;
515470 let new_perm = match val. layout . ty . kind ( ) {
516471 & ty:: Ref ( _, pointee, mutability) =>
517- NewPermission :: from_ref_ty ( pointee, mutability, kind, this) ,
472+ NewPermission :: new ( pointee, Some ( mutability) , kind, this) ,
518473 _ => None ,
519474 } ;
520475 if let Some ( new_perm) = new_perm {
@@ -571,8 +526,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
571526 fn visit_box ( & mut self , box_ty : Ty < ' tcx > , place : & PlaceTy < ' tcx > ) -> InterpResult < ' tcx > {
572527 // Only boxes for the global allocator get any special treatment.
573528 if box_ty. is_box_global ( * self . ecx . tcx ) {
529+ let pointee = place. layout . ty . builtin_deref ( true ) . unwrap ( ) ;
574530 let new_perm =
575- NewPermission :: from_unique_ty ( place . layout . ty , self . kind , self . ecx ) ;
531+ NewPermission :: new ( pointee , /* not a ref */ None , self . kind , self . ecx ) ;
576532 self . retag_ptr_inplace ( place, new_perm) ?;
577533 }
578534 interp_ok ( ( ) )
@@ -591,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
591547 match place. layout . ty . kind ( ) {
592548 & ty:: Ref ( _, pointee, mutability) => {
593549 let new_perm =
594- NewPermission :: from_ref_ty ( pointee, mutability, self . kind , self . ecx ) ;
550+ NewPermission :: new ( pointee, Some ( mutability) , self . kind , self . ecx ) ;
595551 self . retag_ptr_inplace ( place, new_perm) ?;
596552 }
597553 ty:: RawPtr ( _, _) => {
@@ -643,14 +599,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
643599 // never be ReservedIM, the value of the `ty_is_freeze`
644600 // argument doesn't matter
645601 // (`ty_is_freeze || true` in `new_reserved` will always be `true`).
646- freeze_perm : Permission :: new_reserved (
647- /* ty_is_freeze */ true , /* protected */ true ,
648- ) ,
602+ freeze_perm : Permission :: new_reserved_frz ( ) ,
649603 freeze_access : true ,
650- nonfreeze_perm : Permission :: new_reserved (
651- /* ty_is_freeze */ false , /* protected */ true ,
652- ) ,
604+ nonfreeze_perm : Permission :: new_reserved_frz ( ) ,
653605 nonfreeze_access : true ,
606+ outside_perm : Permission :: new_reserved_frz ( ) ,
654607 protector : Some ( ProtectorKind :: StrongProtector ) ,
655608 } ;
656609 this. tb_retag_place ( place, new_perm)
0 commit comments