@@ -5,7 +5,6 @@ mod reuse_pool;
55
66use  std:: cell:: RefCell ; 
77use  std:: cmp:: max; 
8- use  std:: collections:: hash_map:: Entry ; 
98
109use  rand:: Rng ; 
1110
@@ -151,6 +150,95 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
151150        } 
152151    } 
153152
153+     fn  addr_from_alloc_id_uncached ( 
154+         & self , 
155+         global_state :  & mut  GlobalStateInner , 
156+         alloc_id :  AllocId , 
157+         memory_kind :  MemoryKind , 
158+     )  -> InterpResult < ' tcx ,  u64 >  { 
159+         let  ecx = self . eval_context_ref ( ) ; 
160+         let  mut  rng = ecx. machine . rng . borrow_mut ( ) ; 
161+         let  ( size,  align,  kind)  = ecx. get_alloc_info ( alloc_id) ; 
162+         // This is either called immediately after allocation (and then cached), or when 
163+         // adjusting `tcx` pointers (which never get freed). So assert that we are looking 
164+         // at a live allocation. This also ensures that we never re-assign an address to an 
165+         // allocation that previously had an address, but then was freed and the address 
166+         // information was removed. 
167+         assert ! ( !matches!( kind,  AllocKind :: Dead ) ) ; 
168+ 
169+         // This allocation does not have a base address yet, pick or reuse one. 
170+         if  ecx. machine . native_lib . is_some ( )  { 
171+             // In native lib mode, we use the "real" address of the bytes for this allocation. 
172+             // This ensures the interpreted program and native code have the same view of memory. 
173+             let  base_ptr = match  kind { 
174+                 AllocKind :: LiveData  => { 
175+                     if  ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( )  { 
176+                         // For new global allocations, we always pre-allocate the memory to be able use the machine address directly. 
177+                         let  prepared_bytes = MiriAllocBytes :: zeroed ( size,  align) 
178+                             . unwrap_or_else ( || { 
179+                                 panic ! ( "Miri ran out of memory: cannot create allocation of {size:?} bytes" ) 
180+                             } ) ; 
181+                         let  ptr = prepared_bytes. as_ptr ( ) ; 
182+                         // Store prepared allocation space to be picked up for use later. 
183+                         global_state
184+                             . prepared_alloc_bytes 
185+                             . try_insert ( alloc_id,  prepared_bytes) 
186+                             . unwrap ( ) ; 
187+                         ptr
188+                     }  else  { 
189+                         ecx. get_alloc_bytes_unchecked_raw ( alloc_id) ?
190+                     } 
191+                 } 
192+                 AllocKind :: Function  | AllocKind :: VTable  => { 
193+                     // Allocate some dummy memory to get a unique address for this function/vtable. 
194+                     let  alloc_bytes =
195+                         MiriAllocBytes :: from_bytes ( & [ 0u8 ;  1 ] ,  Align :: from_bytes ( 1 ) . unwrap ( ) ) ; 
196+                     let  ptr = alloc_bytes. as_ptr ( ) ; 
197+                     // Leak the underlying memory to ensure it remains unique. 
198+                     std:: mem:: forget ( alloc_bytes) ; 
199+                     ptr
200+                 } 
201+                 AllocKind :: Dead  => unreachable ! ( ) , 
202+             } ; 
203+             // Ensure this pointer's provenance is exposed, so that it can be used by FFI code. 
204+             return  Ok ( base_ptr. expose_provenance ( ) . try_into ( ) . unwrap ( ) ) ; 
205+         } 
206+         // We are not in native lib mode, so we control the addresses ourselves. 
207+         if  let  Some ( ( reuse_addr,  clock) )  =
208+             global_state. reuse . take_addr ( & mut  * rng,  size,  align,  memory_kind,  ecx. active_thread ( ) ) 
209+         { 
210+             if  let  Some ( clock)  = clock { 
211+                 ecx. acquire_clock ( & clock) ; 
212+             } 
213+             Ok ( reuse_addr) 
214+         }  else  { 
215+             // We have to pick a fresh address. 
216+             // Leave some space to the previous allocation, to give it some chance to be less aligned. 
217+             // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. 
218+             let  slack = rng. gen_range ( 0 ..16 ) ; 
219+             // From next_base_addr + slack, round up to adjust for alignment. 
220+             let  base_addr = global_state
221+                 . next_base_addr 
222+                 . checked_add ( slack) 
223+                 . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
224+             let  base_addr = align_addr ( base_addr,  align. bytes ( ) ) ; 
225+ 
226+             // Remember next base address.  If this allocation is zero-sized, leave a gap of at 
227+             // least 1 to avoid two allocations having the same base address. (The logic in 
228+             // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers 
229+             // need to be distinguishable!) 
230+             global_state. next_base_addr  = base_addr
231+                 . checked_add ( max ( size. bytes ( ) ,  1 ) ) 
232+                 . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
233+             // Even if `Size` didn't overflow, we might still have filled up the address space. 
234+             if  global_state. next_base_addr  > ecx. target_usize_max ( )  { 
235+                 throw_exhaust ! ( AddressSpaceFull ) ; 
236+             } 
237+ 
238+             Ok ( base_addr) 
239+         } 
240+     } 
241+ 
154242    fn  addr_from_alloc_id ( 
155243        & self , 
156244        alloc_id :  AllocId , 
@@ -160,98 +248,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
160248        let  mut  global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ; 
161249        let  global_state = & mut  * global_state; 
162250
163-         Ok ( match  global_state. base_addr . entry ( alloc_id)  { 
164-             Entry :: Occupied ( entry)  => * entry. get ( ) , 
165-             Entry :: Vacant ( entry)  => { 
166-                 let  mut  rng = ecx. machine . rng . borrow_mut ( ) ; 
167-                 let  ( size,  align,  kind)  = ecx. get_alloc_info ( alloc_id) ; 
168-                 // This is either called immediately after allocation (and then cached), or when 
169-                 // adjusting `tcx` pointers (which never get freed). So assert that we are looking 
170-                 // at a live allocation. This also ensures that we never re-assign an address to an 
171-                 // allocation that previously had an address, but then was freed and the address 
172-                 // information was removed. 
173-                 assert ! ( !matches!( kind,  AllocKind :: Dead ) ) ; 
174- 
175-                 // This allocation does not have a base address yet, pick or reuse one. 
176-                 let  base_addr = if  ecx. machine . native_lib . is_some ( )  { 
177-                     // In native lib mode, we use the "real" address of the bytes for this allocation. 
178-                     // This ensures the interpreted program and native code have the same view of memory. 
179-                     match  kind { 
180-                         AllocKind :: LiveData  => { 
181-                             let  ptr = if  ecx. tcx . try_get_global_alloc ( alloc_id) . is_some ( )  { 
182-                                 // For new global allocations, we always pre-allocate the memory to be able use the machine address directly. 
183-                                 let  prepared_bytes = MiriAllocBytes :: zeroed ( size,  align) 
184-                                     . unwrap_or_else ( || { 
185-                                         panic ! ( "Miri ran out of memory: cannot create allocation of {size:?} bytes" ) 
186-                                     } ) ; 
187-                                 let  ptr = prepared_bytes. as_ptr ( ) ; 
188-                                     // Store prepared allocation space to be picked up for use later. 
189-                                     global_state. prepared_alloc_bytes . try_insert ( alloc_id,  prepared_bytes) . unwrap ( ) ; 
190-                                 ptr
191-                             }  else  { 
192-                                 ecx. get_alloc_bytes_unchecked_raw ( alloc_id) ?
193-                             } ; 
194-                             // Ensure this pointer's provenance is exposed, so that it can be used by FFI code. 
195-                             ptr. expose_provenance ( ) . try_into ( ) . unwrap ( ) 
196-                         } 
197-                         AllocKind :: Function  | AllocKind :: VTable  => { 
198-                             // Allocate some dummy memory to get a unique address for this function/vtable. 
199-                             let  alloc_bytes = MiriAllocBytes :: from_bytes ( & [ 0u8 ;  1 ] ,  Align :: from_bytes ( 1 ) . unwrap ( ) ) ; 
200-                             // We don't need to expose these bytes as nobody is allowed to access them. 
201-                             let  addr = alloc_bytes. as_ptr ( ) . addr ( ) . try_into ( ) . unwrap ( ) ; 
202-                             // Leak the underlying memory to ensure it remains unique. 
203-                             std:: mem:: forget ( alloc_bytes) ; 
204-                             addr
205-                         } 
206-                         AllocKind :: Dead  => unreachable ! ( ) 
207-                     } 
208-                 }  else  if  let  Some ( ( reuse_addr,  clock) )  = global_state. reuse . take_addr (                     
209-                     & mut  * rng, 
210-                     size, 
211-                     align, 
212-                     memory_kind, 
213-                     ecx. active_thread ( ) , 
214-                 )  { 
215-                     if  let  Some ( clock)  = clock { 
216-                         ecx. acquire_clock ( & clock) ; 
217-                     } 
218-                     reuse_addr
219-                 }  else  { 
220-                     // We have to pick a fresh address. 
221-                     // Leave some space to the previous allocation, to give it some chance to be less aligned. 
222-                     // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. 
223-                     let  slack = rng. gen_range ( 0 ..16 ) ; 
224-                     // From next_base_addr + slack, round up to adjust for alignment. 
225-                     let  base_addr = global_state
226-                         . next_base_addr 
227-                         . checked_add ( slack) 
228-                         . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
229-                     let  base_addr = align_addr ( base_addr,  align. bytes ( ) ) ; 
230- 
231-                     // Remember next base address.  If this allocation is zero-sized, leave a gap 
232-                     // of at least 1 to avoid two allocations having the same base address. 
233-                     // (The logic in `alloc_id_from_addr` assumes unique addresses, and different 
234-                     // function/vtable pointers need to be distinguishable!) 
235-                     global_state. next_base_addr  = base_addr
236-                         . checked_add ( max ( size. bytes ( ) ,  1 ) ) 
237-                         . ok_or_else ( || err_exhaust ! ( AddressSpaceFull ) ) ?; 
238-                     // Even if `Size` didn't overflow, we might still have filled up the address space. 
239-                     if  global_state. next_base_addr  > ecx. target_usize_max ( )  { 
240-                         throw_exhaust ! ( AddressSpaceFull ) ; 
241-                     } 
242- 
243-                     base_addr
244-                 } ; 
245-                 trace ! ( 
246-                     "Assigning base address {:#x} to allocation {:?} (size: {}, align: {})" , 
247-                     base_addr, 
248-                     alloc_id, 
249-                     size. bytes( ) , 
250-                     align. bytes( ) , 
251-                 ) ; 
251+         match  global_state. base_addr . get ( & alloc_id)  { 
252+             Some ( & addr)  => Ok ( addr) , 
253+             None  => { 
254+                 // First time we're looking for the absolute address of this allocation. 
255+                 let  base_addr =
256+                     self . addr_from_alloc_id_uncached ( global_state,  alloc_id,  memory_kind) ?; 
257+                 trace ! ( "Assigning base address {:#x} to allocation {:?}" ,  base_addr,  alloc_id) ; 
252258
253259                // Store address in cache. 
254-                 entry . insert ( base_addr) ; 
260+                 global_state . base_addr . try_insert ( alloc_id ,   base_addr) . unwrap ( ) ; 
255261
256262                // Also maintain the opposite mapping in `int_to_ptr_map`, ensuring we keep it sorted. 
257263                // We have a fast-path for the common case that this address is bigger than all previous ones. 
@@ -269,9 +275,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
269275                } ; 
270276                global_state. int_to_ptr_map . insert ( pos,  ( base_addr,  alloc_id) ) ; 
271277
272-                 base_addr
278+                 Ok ( base_addr) 
273279            } 
274-         } ) 
280+         } 
275281    } 
276282} 
277283
@@ -359,16 +365,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
359365
360366    // This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved 
361367    // memory space in the past, or by doing the pre-allocation right upon being called. 
362-     fn  get_global_alloc_bytes ( & self ,  id :  AllocId ,  kind :  MemoryKind ,  bytes :  & [ u8 ] ,  align :  Align )  -> InterpResult < ' tcx ,  MiriAllocBytes >  { 
368+     fn  get_global_alloc_bytes ( 
369+         & self , 
370+         id :  AllocId , 
371+         kind :  MemoryKind , 
372+         bytes :  & [ u8 ] , 
373+         align :  Align , 
374+     )  -> InterpResult < ' tcx ,  MiriAllocBytes >  { 
363375        let  ecx = self . eval_context_ref ( ) ; 
364-         Ok ( if  ecx. machine . native_lib . is_some ( )  { 
376+         if  ecx. machine . native_lib . is_some ( )  { 
365377            // In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`. 
366-             // This additional call ensures that some `MiriAllocBytes` are always prepared. 
378+             // This additional call ensures that some `MiriAllocBytes` are always prepared, just in case 
379+             // this function gets called before the first time `addr_from_alloc_id` gets called. 
367380            ecx. addr_from_alloc_id ( id,  kind) ?; 
368-             let  mut  global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ; 
369381            // The memory we need here will have already been allocated during an earlier call to 
370382            // `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead 
371383            // fetch the previously prepared bytes from `prepared_alloc_bytes`. 
384+             let  mut  global_state = ecx. machine . alloc_addresses . borrow_mut ( ) ; 
372385            let  mut  prepared_alloc_bytes = global_state
373386                . prepared_alloc_bytes 
374387                . remove ( & id) 
@@ -378,10 +391,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
378391            assert_eq ! ( prepared_alloc_bytes. len( ) ,  bytes. len( ) ) ; 
379392            // Copy allocation contents into prepared memory. 
380393            prepared_alloc_bytes. copy_from_slice ( bytes) ; 
381-             prepared_alloc_bytes
394+             Ok ( prepared_alloc_bytes) 
382395        }  else  { 
383-             MiriAllocBytes :: from_bytes ( std:: borrow:: Cow :: Borrowed ( & * bytes) ,  align) 
384-         } ) 
396+             Ok ( MiriAllocBytes :: from_bytes ( std:: borrow:: Cow :: Borrowed ( bytes) ,  align) ) 
397+         } 
385398    } 
386399
387400    /// When a pointer is used for a memory access, this computes where in which allocation the 
0 commit comments