@@ -125,81 +125,64 @@ pub struct NewPermission {
125
125
/// Whether a read access should be performed on the non-frozen
126
126
/// part on a retag.
127
127
nonfreeze_access : bool ,
128
+ /// Permission for memory outside the range.
129
+ outside_perm : Permission ,
128
130
/// Whether this pointer is part of the arguments of a function call.
129
131
/// `protector` is `Some(_)` for all pointers marked `noalias`.
130
132
protector : Option < ProtectorKind > ,
131
133
}
132
134
133
135
impl < ' 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 (
136
140
pointee : Ty < ' tcx > ,
137
- mutability : Mutability ,
138
- kind : RetagKind ,
141
+ ref_mutability : Option < Mutability > ,
142
+ retag_kind : RetagKind ,
139
143
cx : & crate :: MiriInterpCx < ' tcx > ,
140
144
) -> Option < Self > {
141
145
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 ;
178
148
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
+ } ) ,
203
186
} )
204
187
}
205
188
}
@@ -313,16 +296,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
313
296
314
297
let span = this. machine . current_span ( ) ;
315
298
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
-
326
299
// When adding a new node, the SIFA of its parents needs to be updated, potentially across
327
300
// the entire memory range. For the parts that are being accessed below, the access itself
328
301
// 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> {
350
323
. get_tree_borrows_params ( )
351
324
. precise_interior_mut ;
352
325
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 {
359
329
( new_perm. freeze_perm , new_perm. freeze_access )
360
330
} else {
361
331
( new_perm. nonfreeze_perm , new_perm. nonfreeze_access )
362
332
} ;
363
333
let sifa = perm. strongest_idempotent_foreign_access ( protected) ;
364
- let new_loc = if access {
334
+ if access {
365
335
LocationState :: new_accessed ( perm, sifa)
366
336
} else {
367
337
LocationState :: new_non_accessed ( perm, sifa)
368
- } ;
369
-
370
- for ( _loc_range, loc) in perms_map. iter_mut_all ( ) {
371
- * loc = new_loc;
372
338
}
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)
375
345
} 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
+ ) ;
376
354
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) ;
398
356
for ( _loc_range, loc) in perms_map. iter_mut ( range. start , range. size ) {
399
- * loc = new_loc ;
357
+ * loc = state ;
400
358
}
401
-
402
359
interp_ok ( ( ) )
403
360
} ) ?;
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
407
362
} ;
408
363
409
364
let alloc_extra = this. get_alloc_extra ( alloc_id) ?;
410
365
let mut tree_borrows = alloc_extra. borrow_tracker_tb ( ) . borrow_mut ( ) ;
411
366
412
- for ( perm_range, perm) in perms_map. iter_mut_all ( ) {
367
+ for ( perm_range, perm) in perms_map. iter_all ( ) {
413
368
if perm. is_accessed ( ) {
414
369
// Some reborrows incur a read access to the parent.
415
370
// Adjust range to be relative to allocation start (rather than to `place`).
@@ -447,7 +402,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
447
402
orig_tag,
448
403
new_tag,
449
404
perms_map,
450
- default_perm ,
405
+ new_perm . outside_perm ,
451
406
protected,
452
407
span,
453
408
) ?;
@@ -514,7 +469,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
514
469
let this = self . eval_context_mut ( ) ;
515
470
let new_perm = match val. layout . ty . kind ( ) {
516
471
& ty:: Ref ( _, pointee, mutability) =>
517
- NewPermission :: from_ref_ty ( pointee, mutability, kind, this) ,
472
+ NewPermission :: new ( pointee, Some ( mutability) , kind, this) ,
518
473
_ => None ,
519
474
} ;
520
475
if let Some ( new_perm) = new_perm {
@@ -571,8 +526,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
571
526
fn visit_box ( & mut self , box_ty : Ty < ' tcx > , place : & PlaceTy < ' tcx > ) -> InterpResult < ' tcx > {
572
527
// Only boxes for the global allocator get any special treatment.
573
528
if box_ty. is_box_global ( * self . ecx . tcx ) {
529
+ let pointee = place. layout . ty . builtin_deref ( true ) . unwrap ( ) ;
574
530
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 ) ;
576
532
self . retag_ptr_inplace ( place, new_perm) ?;
577
533
}
578
534
interp_ok ( ( ) )
@@ -591,7 +547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
591
547
match place. layout . ty . kind ( ) {
592
548
& ty:: Ref ( _, pointee, mutability) => {
593
549
let new_perm =
594
- NewPermission :: from_ref_ty ( pointee, mutability, self . kind , self . ecx ) ;
550
+ NewPermission :: new ( pointee, Some ( mutability) , self . kind , self . ecx ) ;
595
551
self . retag_ptr_inplace ( place, new_perm) ?;
596
552
}
597
553
ty:: RawPtr ( _, _) => {
@@ -643,14 +599,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
643
599
// never be ReservedIM, the value of the `ty_is_freeze`
644
600
// argument doesn't matter
645
601
// (`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 ( ) ,
649
603
freeze_access : true ,
650
- nonfreeze_perm : Permission :: new_reserved (
651
- /* ty_is_freeze */ false , /* protected */ true ,
652
- ) ,
604
+ nonfreeze_perm : Permission :: new_reserved_frz ( ) ,
653
605
nonfreeze_access : true ,
606
+ outside_perm : Permission :: new_reserved_frz ( ) ,
654
607
protector : Some ( ProtectorKind :: StrongProtector ) ,
655
608
} ;
656
609
this. tb_retag_place ( place, new_perm)
0 commit comments