@@ -92,27 +92,90 @@ pub enum Permission {
92
92
Disabled ,
93
93
}
94
94
95
- /// An item in the per-location borrow stack.
96
- #[ derive( Copy , Clone , Hash , PartialEq , Eq ) ]
97
- pub struct Item {
98
- /// The permission this item grants.
99
- perm : Permission ,
100
- /// The pointers the permission is granted to.
101
- tag : SbTag ,
102
- /// An optional protector, ensuring the item cannot get popped until `CallId` is over.
103
- protector : Option < CallId > ,
95
+ impl Permission {
96
+ fn to_bits ( self ) -> u64 {
97
+ match self {
98
+ Permission :: Unique => 0 ,
99
+ Permission :: SharedReadWrite => 1 ,
100
+ Permission :: SharedReadOnly => 2 ,
101
+ Permission :: Disabled => 3 ,
102
+ }
103
+ }
104
+
105
+ fn from_bits ( perm : u64 ) -> Self {
106
+ match perm {
107
+ 0 => Permission :: Unique ,
108
+ 1 => Permission :: SharedReadWrite ,
109
+ 2 => Permission :: SharedReadOnly ,
110
+ 3 => Permission :: Disabled ,
111
+ _ => unreachable ! ( ) ,
112
+ }
113
+ }
104
114
}
105
115
106
- impl fmt:: Debug for Item {
107
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
108
- write ! ( f, "[{:?} for {:?}" , self . perm, self . tag) ?;
109
- if let Some ( call) = self . protector {
110
- write ! ( f, " (call {})" , call) ?;
116
+ mod item {
117
+ use super :: { Permission , SbTag } ;
118
+ use std:: fmt;
119
+
120
+ /// An item in the per-location borrow stack.
121
+ #[ derive( Copy , Clone , Hash , PartialEq , Eq ) ]
122
+ pub struct Item ( u64 ) ;
123
+
124
+ // An Item contains 3 bitfields:
125
+ // * Bits 0-61 store an SbTag
126
+ // * Bits 61-63 store a Permission
127
+ // * Bit 64 stores a flag which indicates if we have a protector
128
+ const TAG_MASK : u64 = u64:: MAX >> 3 ;
129
+ const PERM_MASK : u64 = 0x3 << 61 ;
130
+ const PROTECTED_MASK : u64 = 0x1 << 63 ;
131
+
132
+ const PERM_SHIFT : u64 = 61 ;
133
+ const PROTECTED_SHIFT : u64 = 63 ;
134
+
135
+ impl Item {
136
+ pub fn new ( tag : SbTag , perm : Permission , protected : bool ) -> Self {
137
+ assert ! ( tag. 0 . get( ) <= TAG_MASK ) ;
138
+ let packed_tag = tag. 0 . get ( ) ;
139
+ let packed_perm = perm. to_bits ( ) << PERM_SHIFT ;
140
+ let packed_protected = ( protected as u64 ) << PROTECTED_SHIFT ;
141
+
142
+ let new = Self ( packed_tag + packed_perm + packed_protected) ;
143
+
144
+ debug_assert ! ( new. tag( ) == tag) ;
145
+ debug_assert ! ( new. perm( ) == perm) ;
146
+ debug_assert ! ( new. protected( ) == protected) ;
147
+
148
+ new
149
+ }
150
+
151
+ /// The pointers the permission is granted to.
152
+ pub fn tag ( self ) -> SbTag {
153
+ unsafe { SbTag ( std:: num:: NonZeroU64 :: new_unchecked ( self . 0 & TAG_MASK ) ) }
154
+ }
155
+
156
+ /// The permission this item grants.
157
+ pub fn perm ( self ) -> Permission {
158
+ Permission :: from_bits ( ( self . 0 & PERM_MASK ) >> PERM_SHIFT )
159
+ }
160
+
161
+ /// Whether or not there is a protector for this tag
162
+ pub fn protected ( self ) -> bool {
163
+ self . 0 & PROTECTED_MASK > 0
164
+ }
165
+
166
+ /// Set the Permission stored in this Item to Permission::Disabled
167
+ pub fn set_disabled ( & mut self ) {
168
+ self . 0 |= Permission :: Disabled . to_bits ( ) << PERM_SHIFT ;
169
+ }
170
+ }
171
+
172
+ impl fmt:: Debug for Item {
173
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
174
+ write ! ( f, "[{:?} for {:?}]" , self . perm( ) , self . tag( ) )
111
175
}
112
- write ! ( f, "]" ) ?;
113
- Ok ( ( ) )
114
176
}
115
177
}
178
+ pub use item:: Item ;
116
179
117
180
/// Extra per-allocation state.
118
181
#[ derive( Clone , Debug ) ]
@@ -138,6 +201,8 @@ pub struct GlobalStateInner {
138
201
next_call_id : CallId ,
139
202
/// Those call IDs corresponding to functions that are still running.
140
203
active_calls : FxHashSet < CallId > ,
204
+ /// All tags currently protected
205
+ pub ( crate ) protected_tags : FxHashMap < SbTag , CallId > ,
141
206
/// The pointer ids to trace
142
207
tracked_pointer_tags : HashSet < SbTag > ,
143
208
/// The call ids to trace
@@ -202,6 +267,7 @@ impl GlobalStateInner {
202
267
base_ptr_tags : FxHashMap :: default ( ) ,
203
268
next_call_id : NonZeroU64 :: new ( 1 ) . unwrap ( ) ,
204
269
active_calls : FxHashSet :: default ( ) ,
270
+ protected_tags : FxHashMap :: default ( ) ,
205
271
tracked_pointer_tags,
206
272
tracked_call_ids,
207
273
retag_fields,
@@ -230,10 +296,6 @@ impl GlobalStateInner {
230
296
assert ! ( self . active_calls. remove( & id) ) ;
231
297
}
232
298
233
- fn is_active ( & self , id : CallId ) -> bool {
234
- self . active_calls . contains ( & id)
235
- }
236
-
237
299
pub fn base_ptr_tag ( & mut self , id : AllocId ) -> SbTag {
238
300
self . base_ptr_tags . get ( & id) . copied ( ) . unwrap_or_else ( || {
239
301
let tag = self . new_ptr ( ) ;
@@ -287,7 +349,7 @@ impl<'tcx> Stack {
287
349
/// Find the first write-incompatible item above the given one --
288
350
/// i.e, find the height to which the stack will be truncated when writing to `granting`.
289
351
fn find_first_write_incompatible ( & self , granting : usize ) -> usize {
290
- let perm = self . get ( granting) . unwrap ( ) . perm ;
352
+ let perm = self . get ( granting) . unwrap ( ) . perm ( ) ;
291
353
match perm {
292
354
Permission :: SharedReadOnly => bug ! ( "Cannot use SharedReadOnly for writing" ) ,
293
355
Permission :: Disabled => bug ! ( "Cannot use Disabled for anything" ) ,
@@ -299,7 +361,7 @@ impl<'tcx> Stack {
299
361
// The SharedReadWrite *just* above us are compatible, to skip those.
300
362
let mut idx = granting + 1 ;
301
363
while let Some ( item) = self . get ( idx) {
302
- if item. perm == Permission :: SharedReadWrite {
364
+ if item. perm ( ) == Permission :: SharedReadWrite {
303
365
// Go on.
304
366
idx += 1 ;
305
367
} else {
@@ -326,31 +388,36 @@ impl<'tcx> Stack {
326
388
global : & GlobalStateInner ,
327
389
alloc_history : & mut AllocHistory ,
328
390
) -> InterpResult < ' tcx > {
329
- if global. tracked_pointer_tags . contains ( & item. tag ) {
391
+ if global. tracked_pointer_tags . contains ( & item. tag ( ) ) {
330
392
register_diagnostic ( NonHaltingDiagnostic :: PoppedPointerTag (
331
393
* item,
332
394
provoking_access. map ( |( tag, _alloc_range, _size, access) | ( tag, access) ) ,
333
395
) ) ;
334
396
}
335
397
336
- if let Some ( call) = item. protector {
337
- if global. is_active ( call) {
338
- if let Some ( ( tag, _alloc_range, _offset, _access) ) = provoking_access {
339
- Err ( err_sb_ub (
340
- format ! (
341
- "not granting access to tag {:?} because incompatible item is protected: {:?}" ,
342
- tag, item
343
- ) ,
344
- None ,
345
- tag. and_then ( |tag| alloc_history. get_logs_relevant_to ( tag, Some ( item. tag ) ) ) ,
346
- ) ) ?
347
- } else {
348
- Err ( err_sb_ub (
349
- format ! ( "deallocating while item is protected: {:?}" , item) ,
350
- None ,
351
- None ,
352
- ) ) ?
353
- }
398
+ if !item. protected ( ) {
399
+ return Ok ( ( ) ) ;
400
+ }
401
+
402
+ if let Some ( call_id) = global. protected_tags . get ( & item. tag ( ) ) {
403
+ if let Some ( ( tag, _alloc_range, _offset, _access) ) = provoking_access {
404
+ Err ( err_sb_ub (
405
+ format ! (
406
+ "not granting access to tag {:?} because incompatible item is protected: {:?} (call {:?})" ,
407
+ tag, item, call_id
408
+ ) ,
409
+ None ,
410
+ tag. and_then ( |tag| alloc_history. get_logs_relevant_to ( tag, Some ( item. tag ( ) ) ) ) ,
411
+ ) ) ?
412
+ } else {
413
+ Err ( err_sb_ub (
414
+ format ! (
415
+ "deallocating while item is protected: {:?} (call {:?})" ,
416
+ item, call_id
417
+ ) ,
418
+ None ,
419
+ None ,
420
+ ) ) ?
354
421
}
355
422
}
356
423
Ok ( ( ) )
@@ -400,7 +467,7 @@ impl<'tcx> Stack {
400
467
global,
401
468
alloc_history,
402
469
) ?;
403
- alloc_history. log_invalidation ( item. tag , alloc_range, current_span) ;
470
+ alloc_history. log_invalidation ( item. tag ( ) , alloc_range, current_span) ;
404
471
Ok ( ( ) )
405
472
} ) ?;
406
473
} else {
@@ -426,7 +493,7 @@ impl<'tcx> Stack {
426
493
global,
427
494
alloc_history,
428
495
) ?;
429
- alloc_history. log_invalidation ( item. tag , alloc_range, current_span) ;
496
+ alloc_history. log_invalidation ( item. tag ( ) , alloc_range, current_span) ;
430
497
Ok ( ( ) )
431
498
} ) ?;
432
499
}
@@ -439,9 +506,9 @@ impl<'tcx> Stack {
439
506
for i in 0 ..self . len ( ) {
440
507
let item = self . get ( i) . unwrap ( ) ;
441
508
// Skip disabled items, they cannot be matched anyway.
442
- if !matches ! ( item. perm, Permission :: Disabled ) {
509
+ if !matches ! ( item. perm( ) , Permission :: Disabled ) {
443
510
// We are looking for a strict upper bound, so add 1 to this tag.
444
- max = cmp:: max ( item. tag . 0 . checked_add ( 1 ) . unwrap ( ) , max) ;
511
+ max = cmp:: max ( item. tag ( ) . 0 . checked_add ( 1 ) . unwrap ( ) , max) ;
445
512
}
446
513
}
447
514
if let Some ( unk) = self . unknown_bottom ( ) {
@@ -505,7 +572,7 @@ impl<'tcx> Stack {
505
572
) -> InterpResult < ' tcx > {
506
573
// Figure out which access `perm` corresponds to.
507
574
let access =
508
- if new. perm . grants ( AccessKind :: Write ) { AccessKind :: Write } else { AccessKind :: Read } ;
575
+ if new. perm ( ) . grants ( AccessKind :: Write ) { AccessKind :: Write } else { AccessKind :: Read } ;
509
576
510
577
// Now we figure out which item grants our parent (`derived_from`) this kind of access.
511
578
// We use that to determine where to put the new item.
@@ -517,7 +584,7 @@ impl<'tcx> Stack {
517
584
// Compute where to put the new item.
518
585
// Either way, we ensure that we insert the new item in a way such that between
519
586
// `derived_from` and the new one, there are only items *compatible with* `derived_from`.
520
- let new_idx = if new. perm == Permission :: SharedReadWrite {
587
+ let new_idx = if new. perm ( ) == Permission :: SharedReadWrite {
521
588
assert ! (
522
589
access == AccessKind :: Write ,
523
590
"this case only makes sense for stack-like accesses"
@@ -578,7 +645,7 @@ impl<'tcx> Stack {
578
645
impl < ' tcx > Stacks {
579
646
/// Creates new stack with initial tag.
580
647
fn new ( size : Size , perm : Permission , tag : SbTag ) -> Self {
581
- let item = Item { perm, tag , protector : None } ;
648
+ let item = Item :: new ( tag , perm, false ) ;
582
649
let stack = Stack :: new ( item) ;
583
650
584
651
Stacks {
@@ -808,7 +875,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
808
875
} ) ;
809
876
}
810
877
811
- let protector = if protect { Some ( this. frame ( ) . extra . call_id ) } else { None } ;
812
878
trace ! (
813
879
"reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}" ,
814
880
kind,
@@ -819,6 +885,22 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
819
885
size. bytes( )
820
886
) ;
821
887
888
+ // FIXME: This just protects everything, which is wrong. At the very least, we should not
889
+ // protect anything that contains an UnsafeCell.
890
+ if protect {
891
+ this. frame_mut ( ) . extra . protected_tags . push ( new_tag) ;
892
+ let call_id = this. frame ( ) . extra . call_id ;
893
+ this. machine
894
+ . stacked_borrows
895
+ . as_mut ( )
896
+ . unwrap ( )
897
+ . get_mut ( )
898
+ . protected_tags
899
+ . insert ( new_tag, call_id) ;
900
+ }
901
+ // FIXME: can't hold the current span handle across the borrows of self above
902
+ let current_span = & mut this. machine . current_span ( ) ;
903
+
822
904
// Update the stacks.
823
905
// Make sure that raw pointers and mutable shared references are reborrowed "weak":
824
906
// There could be existing unique pointers reborrowed from them that should remain valid!
@@ -855,14 +937,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
855
937
} else {
856
938
Permission :: SharedReadWrite
857
939
} ;
858
- let protector = if frozen {
859
- protector
940
+ let protected = if frozen {
941
+ protect
860
942
} else {
861
943
// We do not protect inside UnsafeCell.
862
944
// This fixes https://github.com/rust-lang/rust/issues/55005.
863
- None
945
+ false
864
946
} ;
865
- let item = Item { perm , tag : new_tag, protector } ;
947
+ let item = Item :: new ( new_tag, perm , protected ) ;
866
948
let mut global = this. machine . stacked_borrows . as_ref ( ) . unwrap ( ) . borrow_mut ( ) ;
867
949
stacked_borrows. for_each ( range, |offset, stack, history, exposed_tags| {
868
950
stack. grant (
@@ -888,7 +970,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
888
970
. as_mut ( )
889
971
. expect ( "we should have Stacked Borrows data" )
890
972
. borrow_mut ( ) ;
891
- let item = Item { perm , tag : new_tag, protector } ;
973
+ let item = Item :: new ( new_tag, perm , protect ) ;
892
974
let range = alloc_range ( base_offset, size) ;
893
975
let mut global = machine. stacked_borrows . as_ref ( ) . unwrap ( ) . borrow_mut ( ) ;
894
976
let current_span = & mut machine. current_span ( ) ; // `get_alloc_extra_mut` invalidated our old `current_span`
0 commit comments