@@ -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 exposed.
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,13 @@ 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 either called immediately after allocation (and then cached), or when
127
+ // adjusting `tcx` pointers (which never get freed). So assert that we are looking
128
+ // at a live allocation. This also ensures that we never re-assign an address to an
129
+ // allocation that previously had an address, but then was freed and the address
130
+ // information was removed.
131
+ assert ! ( !matches!( kind, AllocKind :: Dead ) ) ;
130
132
131
133
// This allocation does not have a base address yet, pick one.
132
134
// Leave some space to the previous allocation, to give it some chance to be less aligned.
@@ -162,6 +164,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
162
164
if global_state. next_base_addr > ecx. target_usize_max ( ) {
163
165
throw_exhaust ! ( AddressSpaceFull ) ;
164
166
}
167
+ // Also maintain the opposite mapping in `int_to_ptr_map`.
165
168
// Given that `next_base_addr` increases in each allocation, pushing the
166
169
// corresponding tuple keeps `int_to_ptr_map` sorted
167
170
global_state. int_to_ptr_map . push ( ( base_addr, alloc_id) ) ;
@@ -257,7 +260,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
257
260
} ;
258
261
259
262
// This cannot fail: since we already have a pointer with that provenance, rel_ptr_to_addr
260
- // must have been called in the past.
263
+ // must have been called in the past, so we can just look up the address in the map .
261
264
let base_addr = ecx. addr_from_alloc_id ( alloc_id) . unwrap ( ) ;
262
265
263
266
// Wrapping "addr - base_addr"
@@ -270,6 +273,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
270
273
}
271
274
}
272
275
276
+ impl GlobalStateInner {
277
+ pub fn free_alloc_id ( & mut self , dead_id : AllocId ) {
278
+ // We can *not* remove this from `base_addr`, since `addr_from_alloc_id` is called on each
279
+ // attempt at a memory access to determine the allocation ID and offset -- and there can
280
+ // still be pointers with `dead_id` that one can attempt to use for a memory access.
281
+ // However, we *can* remove it from `int_to_ptr_map`, since any wildcard pointers that exist
282
+ // can no longer actually be accessing that address. This ensures `alloc_id_from_addr` never
283
+ // returns a dead allocation.
284
+ self . int_to_ptr_map . retain ( |& ( _, id) | id != dead_id) ;
285
+ // We can also remove it from `exposed`, since this allocation can anyway not be returned by
286
+ // `alloc_id_from_addr` any more.
287
+ self . exposed . remove ( & dead_id) ;
288
+ }
289
+ }
290
+
273
291
#[ cfg( test) ]
274
292
mod tests {
275
293
use super :: * ;
0 commit comments