]> git.lizzy.rs Git - rust.git/blob - src/intptrcast.rs
tweak inttoptr allocation behavior
[rust.git] / src / intptrcast.rs
1 use std::cell::{Cell, RefCell};
2
3 use rand::Rng;
4
5 use rustc::mir::interpret::{AllocId, Pointer, InterpResult};
6 use rustc_mir::interpret::Memory;
7 use rustc_target::abi::Size;
8
9 use crate::stacked_borrows::Tag;
10 use crate::Evaluator;
11
12 pub type MemoryExtra = RefCell<GlobalState>;
13
14 #[derive(Clone, Debug, Default)]
15 pub struct AllocExtra {
16     base_addr: Cell<Option<u64>>
17 }
18
19 #[derive(Clone, Debug)]
20 pub struct GlobalState {
21     /// This is used as a map between the address of each allocation and its `AllocId`.
22     /// It is always sorted
23     pub int_to_ptr_map: Vec<(u64, AllocId)>,
24     /// This is used as a memory address when a new pointer is casted to an integer. It
25     /// is always larger than any address that was previously made part of a block.
26     pub next_base_addr: u64,
27 }
28
29 impl Default for GlobalState {
30     // FIXME: Query the page size in the future
31     fn default() -> Self {
32         GlobalState {
33             int_to_ptr_map: Vec::default(),
34             next_base_addr: 2u64.pow(16)
35         }
36     }
37 }
38
39 impl<'mir, 'tcx> GlobalState {
40     pub fn int_to_ptr(
41         int: u64,
42         memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
43     ) -> InterpResult<'tcx, Pointer<Tag>> {
44         let global_state = memory.extra.intptrcast.borrow();
45         
46         match global_state.int_to_ptr_map.binary_search_by_key(&int, |(addr, _)| *addr) {
47             Ok(pos) => {
48                 let (_, alloc_id) = global_state.int_to_ptr_map[pos];
49                 // `int` is equal to the starting address for an allocation, the offset should be
50                 // zero. The pointer is untagged because it was created from a cast
51                 Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(0), Tag::Untagged))
52             },
53             Err(0) => err!(DanglingPointerDeref), 
54             Err(pos) => {
55                 // This is the largest of the adresses smaller than `int`,
56                 // i.e. the greatest lower bound (glb)
57                 let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
58                 // This never overflows because `int >= glb`
59                 let offset = int - glb;
60                 // If the offset exceeds the size of the allocation, this access is illegal
61                 if offset <= memory.get(alloc_id)?.bytes.len() as u64 {
62                     // This pointer is untagged because it was created from a cast
63                     Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(offset), Tag::Untagged))
64                 } else {
65                     err!(DanglingPointerDeref)
66                 } 
67             }
68         }
69     }
70
71     pub fn ptr_to_int(
72         ptr: Pointer<Tag>,
73         memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
74     ) -> InterpResult<'tcx, u64> {
75         let mut global_state = memory.extra.intptrcast.borrow_mut();
76
77         let alloc = memory.get(ptr.alloc_id)?;
78         let align = alloc.align.bytes();
79
80         let base_addr = match alloc.extra.intptrcast.base_addr.get() { 
81             Some(base_addr) => base_addr,
82             None => {
83                 // This allocation does not have a base address yet, pick one.
84                 // Leave some space to the previous allocation, to give it some chance to be less aligned.
85                 let slack = {
86                     let mut rng = memory.extra.rng.as_ref().unwrap().borrow_mut();
87                     rng.gen_range(0, align)
88                 };
89                 // From next_base_addr + slack, round up to adjust for alignment.
90                 let base_addr = Self::align_addr(global_state.next_base_addr + slack, align);
91                 alloc.extra.intptrcast.base_addr.set(Some(base_addr));
92
93                 // Remember next base address.
94                 global_state.next_base_addr = base_addr + alloc.bytes.len() as u64;
95                 // Given that `next_base_addr` increases in each allocation, pushing the
96                 // corresponding tuple keeps `int_to_ptr_map` sorted
97                 global_state.int_to_ptr_map.push((base_addr, ptr.alloc_id)); 
98
99                 base_addr
100             }
101         };
102
103         debug_assert_eq!(base_addr % align, 0); // sanity check
104         Ok(base_addr + ptr.offset.bytes())
105     }
106
107     /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
108     /// of `align` that is larger or equal to `addr`
109     fn align_addr(addr: u64, align: u64) -> u64 {
110         if addr % align == 0 {
111             addr
112         } else {
113             addr + align - addr % align
114         }
115     }
116 }
117
118 #[cfg(test)]
119 mod tests {
120     use super::*;
121
122     #[test]
123     fn test_align_addr() {
124         assert_eq!(GlobalState::align_addr(37, 4), 40);
125         assert_eq!(GlobalState::align_addr(44, 4), 44);
126     }
127 }