@@ -26,8 +26,10 @@ pub type GlobalState = RefCell<GlobalStateInner>;
26
26
27
27
#[ derive( Clone , Debug ) ]
28
28
pub struct GlobalStateInner {
29
- /// This is used as a map between the address of each allocation and its `AllocId`.
30
- /// It is always sorted
29
+ /// This is used as a map between the address of each allocation and its `AllocId`. It is always
30
+ /// sorted. We cannot use a `HashMap` since we can be given an address that is offset from the
31
+ /// base address, and we need to find the `AllocId` it belongs to.
32
+ /// This is not the *full* inverse of `base_addr`; dead allocations have been removed.
31
33
int_to_ptr_map : Vec < ( u64 , AllocId ) > ,
32
34
/// The base address for each allocation. We cannot put that into
33
35
/// `AllocExtra` because function pointers also have a base address, and
@@ -102,18 +104,14 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
102
104
}
103
105
} ?;
104
106
105
- // We only use this provenance if it has been exposed, *and* is still live .
107
+ // We only use this provenance if it has been exposede .
106
108
if global_state. exposed . contains ( & alloc_id) {
107
- let ( _size, _align, kind) = ecx. get_alloc_info ( alloc_id) ;
108
- match kind {
109
- AllocKind :: LiveData | AllocKind :: Function | AllocKind :: VTable => {
110
- return Some ( alloc_id) ;
111
- }
112
- AllocKind :: Dead => { }
113
- }
109
+ // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
110
+ debug_assert ! ( !matches!( ecx. get_alloc_info( alloc_id) . 2 , AllocKind :: Dead ) ) ;
111
+ Some ( alloc_id)
112
+ } else {
113
+ None
114
114
}
115
-
116
- None
117
115
}
118
116
119
117
fn addr_from_alloc_id ( & self , alloc_id : AllocId ) -> InterpResult < ' tcx , u64 > {
@@ -124,9 +122,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
124
122
Ok ( match global_state. base_addr . entry ( alloc_id) {
125
123
Entry :: Occupied ( entry) => * entry. get ( ) ,
126
124
Entry :: Vacant ( entry) => {
127
- // There is nothing wrong with a raw pointer being cast to an integer only after
128
- // it became dangling. Hence we allow dead allocations.
129
- let ( size, align, _kind) = ecx. get_alloc_info ( alloc_id) ;
125
+ let ( size, align, kind) = ecx. get_alloc_info ( alloc_id) ;
126
+ // This is only called just after allocation, and when adjusting `tcx` pointers. So
127
+ // the allocation can never be dead here. This also ensures that we never re-assign
128
+ // an address to an allocation that previously had an address, but then was freed
129
+ // and the address information was removed.
130
+ assert ! ( !matches!( kind, AllocKind :: Dead ) ) ;
130
131
131
132
// This allocation does not have a base address yet, pick one.
132
133
// Leave some space to the previous allocation, to give it some chance to be less aligned.
@@ -162,6 +163,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
162
163
if global_state. next_base_addr > ecx. target_usize_max ( ) {
163
164
throw_exhaust ! ( AddressSpaceFull ) ;
164
165
}
166
+ // Also maintain the opposite mapping in `int_to_ptr_map`.
165
167
// Given that `next_base_addr` increases in each allocation, pushing the
166
168
// corresponding tuple keeps `int_to_ptr_map` sorted
167
169
global_state. int_to_ptr_map . push ( ( base_addr, alloc_id) ) ;
@@ -246,7 +248,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
246
248
} ;
247
249
248
250
// This cannot fail: since we already have a pointer with that provenance, rel_ptr_to_addr
249
- // must have been called in the past.
251
+ // must have been called in the past, so we can just look up the address in the map .
250
252
let base_addr = ecx. addr_from_alloc_id ( alloc_id) . unwrap ( ) ;
251
253
252
254
// Wrapping "addr - base_addr"
@@ -260,6 +262,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
260
262
}
261
263
}
262
264
265
+ impl GlobalStateInner {
266
+ pub fn free_alloc_id ( & mut self , dead_id : AllocId ) {
267
+ // We can *not* remove this from `base_addr`, since `addr_from_alloc_id` is called on each
268
+ // attempt at a memory access to determine the allocation ID and offset -- and there can
269
+ // still be pointers with `dead_id` that one can attempt to use for a memory access.
270
+ // However, we *can* remove it from `int_to_ptr_map`, since any wildcard pointers that exist
271
+ // can no longer actually be accessing that address. This ensures `alloc_id_from_addr` never
272
+ // returns a dead allocation.
273
+ self . int_to_ptr_map . retain ( |& ( _, id) | id != dead_id) ;
274
+ }
275
+ }
276
+
263
277
#[ cfg( test) ]
264
278
mod tests {
265
279
use super :: * ;
0 commit comments