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