@@ -26,8 +26,10 @@ pub type GlobalState = RefCell<GlobalStateInner>;
2626
2727#[ derive( Clone , Debug ) ]
2828pub 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.
3133 int_to_ptr_map : Vec < ( u64 , AllocId ) > ,
3234 /// The base address for each allocation. We cannot put that into
3335 /// `AllocExtra` because function pointers also have a base address, and
@@ -102,18 +104,14 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
102104 }
103105 } ?;
104106
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.
106108 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
114114 }
115-
116- None
117115 }
118116
119117 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> {
124122 Ok ( match global_state. base_addr . entry ( alloc_id) {
125123 Entry :: Occupied ( entry) => * entry. get ( ) ,
126124 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 ) ) ;
130132
131133 // This allocation does not have a base address yet, pick one.
132134 // 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> {
162164 if global_state. next_base_addr > ecx. target_usize_max ( ) {
163165 throw_exhaust ! ( AddressSpaceFull ) ;
164166 }
167+ // Also maintain the opposite mapping in `int_to_ptr_map`.
165168 // Given that `next_base_addr` increases in each allocation, pushing the
166169 // corresponding tuple keeps `int_to_ptr_map` sorted
167170 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> {
257260 } ;
258261
259262 // 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 .
261264 let base_addr = ecx. addr_from_alloc_id ( alloc_id) . unwrap ( ) ;
262265
263266 // Wrapping "addr - base_addr"
@@ -270,6 +273,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
270273 }
271274}
272275
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+
273291#[ cfg( test) ]
274292mod tests {
275293 use super :: * ;
0 commit comments