]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #779 - christianpoveda:intptrcast-model, r=RalfJung,oli-obk
authorbors <bors@rust-lang.org>
Wed, 26 Jun 2019 14:52:03 +0000 (14:52 +0000)
committerbors <bors@rust-lang.org>
Wed, 26 Jun 2019 14:52:03 +0000 (14:52 +0000)
Implement intptrcast methods

cc https://github.com/rust-lang/miri/issues/224

src/fn_call.rs
src/intptrcast.rs [new file with mode: 0644]
src/lib.rs
src/memory.rs [new file with mode: 0644]
src/operator.rs
src/stacked_borrows.rs
tests/compile-fail/intptrcast_cast_int_to_fn_ptr.rs [new file with mode: 0644]
tests/compile-fail/intptrcast_null_pointer_deref.rs [new file with mode: 0644]
tests/compile-fail/intptrcast_wild_pointer_deref.rs [new file with mode: 0644]
tests/run-pass/intptrcast.rs [new file with mode: 0644]

index b053d8c51e70ca66e6536b1f588dd0a1d25da493..e2a4daa6f385353757dbcfa7b5838d03fb4d0c32 100644 (file)
@@ -980,7 +980,7 @@ fn gen_random<'mir, 'tcx>(
     }
     let ptr = dest.to_ptr()?;
 
-    let data = match &mut this.machine.rng {
+    let data = match &mut this.memory_mut().extra.rng {
         Some(rng) => {
             let mut data = vec![0; len];
             rng.fill_bytes(&mut data);
diff --git a/src/intptrcast.rs b/src/intptrcast.rs
new file mode 100644 (file)
index 0000000..ad0489f
--- /dev/null
@@ -0,0 +1,100 @@
+use std::cell::{Cell, RefCell};
+
+use rustc::mir::interpret::{AllocId, Pointer, InterpResult};
+use rustc_mir::interpret::Memory;
+use rustc_target::abi::Size;
+
+use crate::stacked_borrows::Tag;
+use crate::Evaluator;
+
+pub type MemoryExtra = RefCell<GlobalState>;
+
+#[derive(Clone, Debug, Default)]
+pub struct AllocExtra {
+    base_addr: Cell<Option<u64>>
+}
+
+#[derive(Clone, Debug)]
+pub struct GlobalState {
+    /// This is used as a map between the address of each allocation and its `AllocId`.
+    /// It is always sorted
+    pub int_to_ptr_map: Vec<(u64, AllocId)>,
+    /// This is used as a memory address when a new pointer is casted to an integer. It
+    /// is always larger than any address that was previously made part of a block.
+    pub next_base_addr: u64,
+}
+
+impl Default for GlobalState {
+    // FIXME: Query the page size in the future
+    fn default() -> Self {
+        GlobalState {
+            int_to_ptr_map: Vec::default(),
+            next_base_addr: 2u64.pow(16)
+        }
+    }
+}
+
+impl<'mir, 'tcx> GlobalState {
+    pub fn int_to_ptr(
+        int: u64,
+        memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
+    ) -> InterpResult<'tcx, Pointer<Tag>> {
+        let global_state = memory.extra.intptrcast.borrow();
+        
+        match global_state.int_to_ptr_map.binary_search_by_key(&int, |(addr, _)| *addr) {
+            Ok(pos) => {
+                let (_, alloc_id) = global_state.int_to_ptr_map[pos];
+                // `int` is equal to the starting address for an allocation, the offset should be
+                // zero. The pointer is untagged because it was created from a cast
+                Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(0), Tag::Untagged))
+            },
+            Err(0) => err!(DanglingPointerDeref), 
+            Err(pos) => {
+                // This is the largest of the adresses smaller than `int`,
+                // i.e. the greatest lower bound (glb)
+                let (glb, alloc_id) = global_state.int_to_ptr_map[pos - 1];
+                // This never overflows because `int >= glb`
+                let offset = int - glb;
+                // If the offset exceeds the size of the allocation, this access is illegal
+                if offset <= memory.get(alloc_id)?.bytes.len() as u64 {
+                    // This pointer is untagged because it was created from a cast
+                    Ok(Pointer::new_with_tag(alloc_id, Size::from_bytes(offset), Tag::Untagged))
+                } else {
+                    err!(DanglingPointerDeref)
+                } 
+            }
+        }
+    }
+
+    pub fn ptr_to_int(
+        ptr: Pointer<Tag>,
+        memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
+    ) -> InterpResult<'tcx, u64> {
+        let mut global_state = memory.extra.intptrcast.borrow_mut();
+
+        let alloc = memory.get(ptr.alloc_id)?;
+
+        let base_addr = match alloc.extra.intptrcast.base_addr.get() { 
+            Some(base_addr) => base_addr,
+            None => {
+                // This allocation does not have a base address yet, pick one.
+                let base_addr = Self::align_addr(global_state.next_base_addr, alloc.align.bytes());
+                global_state.next_base_addr = base_addr + alloc.bytes.len() as u64;
+                alloc.extra.intptrcast.base_addr.set(Some(base_addr));
+                // Given that `next_base_addr` increases in each allocation, pushing the
+                // corresponding tuple keeps `int_to_ptr_map` sorted
+                global_state.int_to_ptr_map.push((base_addr, ptr.alloc_id)); 
+
+                base_addr
+            }
+        };
+
+        Ok(base_addr + ptr.offset.bytes())
+    }
+
+    /// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
+    /// of `align` that is strictly larger to `addr`
+    fn align_addr(addr: u64, align: u64) -> u64 {
+        addr + align - addr % align
+    }
+}
index 9072f141f89d9d3759873c71791352741b03f83f..6b1ada69d3975dfc38e9e8e8e6acd833a1699f55 100644 (file)
@@ -20,6 +20,8 @@
 mod range_map;
 mod mono_hash_map;
 mod stacked_borrows;
+mod intptrcast;
+mod memory;
 
 use std::collections::HashMap;
 use std::borrow::Cow;
@@ -48,6 +50,7 @@
 pub use crate::helpers::{EvalContextExt as HelpersEvalContextExt};
 use crate::mono_hash_map::MonoHashMap;
 pub use crate::stacked_borrows::{EvalContextExt as StackedBorEvalContextExt};
+use crate::memory::AllocExtra;
 
 // Used by priroda.
 pub use crate::stacked_borrows::{Tag, Permission, Stack, Stacks, Item};
@@ -79,9 +82,12 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
     let mut ecx = InterpretCx::new(
         tcx.at(syntax::source_map::DUMMY_SP),
         ty::ParamEnv::reveal_all(),
-        Evaluator::new(config.validate, config.seed),
+        Evaluator::new(config.validate),
     );
 
+    // FIXME: InterpretCx::new should take an initial MemoryExtra
+    ecx.memory_mut().extra.rng = config.seed.map(StdRng::seed_from_u64);
+    
     let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
     let main_mir = ecx.load_mir(main_instance.def)?;
 
@@ -205,7 +211,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
             cur_ptr = cur_ptr.offset(char_size, tcx)?;
         }
     }
-
     assert!(args.next().is_none(), "start lang item has more arguments than expected");
 
     Ok(ecx)
@@ -341,14 +347,10 @@ pub struct Evaluator<'tcx> {
 
     /// Whether to enforce the validity invariant.
     pub(crate) validate: bool,
-
-    /// The random number generator to use if Miri
-    /// is running in non-deterministic mode
-    pub(crate) rng: Option<StdRng>
 }
 
 impl<'tcx> Evaluator<'tcx> {
-    fn new(validate: bool, seed: Option<u64>) -> Self {
+    fn new(validate: bool) -> Self {
         Evaluator {
             env_vars: HashMap::default(),
             argc: None,
@@ -357,7 +359,6 @@ fn new(validate: bool, seed: Option<u64>) -> Self {
             last_error: 0,
             tls: TlsData::default(),
             validate,
-            rng: seed.map(|s| StdRng::seed_from_u64(s))
         }
     }
 }
@@ -386,8 +387,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
     type MemoryKinds = MiriMemoryKind;
 
     type FrameExtra = stacked_borrows::CallId;
-    type MemoryExtra = stacked_borrows::MemoryState;
-    type AllocExtra = stacked_borrows::Stacks;
+    type MemoryExtra = memory::MemoryExtra;
+    type AllocExtra = memory::AllocExtra;
     type PointerTag = Tag;
 
     type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;
@@ -512,17 +513,17 @@ fn tag_allocation<'b>(
     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         let alloc = alloc.into_owned();
-        let (extra, base_tag) = Stacks::new_allocation(
+        let (stacks, base_tag) = Stacks::new_allocation(
             id,
             Size::from_bytes(alloc.bytes.len() as u64),
-            Rc::clone(&memory.extra),
+            Rc::clone(&memory.extra.stacked_borrows),
             kind,
         );
         if kind != MiriMemoryKind::Static.into() {
             assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
             // Now we can rely on the inner pointers being static, too.
         }
-        let mut memory_extra = memory.extra.borrow_mut();
+        let mut memory_extra = memory.extra.stacked_borrows.borrow_mut();
         let alloc: Allocation<Tag, Self::AllocExtra> = Allocation {
             bytes: alloc.bytes,
             relocations: Relocations::from_presorted(
@@ -535,7 +536,10 @@ fn tag_allocation<'b>(
             undef_mask: alloc.undef_mask,
             align: alloc.align,
             mutability: alloc.mutability,
-            extra,
+            extra: AllocExtra {
+                stacked_borrows: stacks,
+                intptrcast: Default::default(),
+            },
         };
         (Cow::Owned(alloc), base_tag)
     }
@@ -545,7 +549,7 @@ fn tag_static_base_pointer(
         id: AllocId,
         memory: &Memory<'mir, 'tcx, Self>,
     ) -> Self::PointerTag {
-        memory.extra.borrow_mut().static_base_ptr(id)
+        memory.extra.stacked_borrows.borrow_mut().static_base_ptr(id)
     }
 
     #[inline(always)]
@@ -570,7 +574,7 @@ fn retag(
     fn stack_push(
         ecx: &mut InterpretCx<'mir, 'tcx, Self>,
     ) -> InterpResult<'tcx, stacked_borrows::CallId> {
-        Ok(ecx.memory().extra.borrow_mut().new_call())
+        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().new_call())
     }
 
     #[inline(always)]
@@ -578,6 +582,30 @@ fn stack_pop(
         ecx: &mut InterpretCx<'mir, 'tcx, Self>,
         extra: stacked_borrows::CallId,
     ) -> InterpResult<'tcx> {
-        Ok(ecx.memory().extra.borrow_mut().end_call(extra))
+        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().end_call(extra))
+    }
+
+    fn int_to_ptr(
+        int: u64,
+        memory: &Memory<'mir, 'tcx, Self>,
+    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
+        if int == 0 {
+            err!(InvalidNullPointerUsage)
+        } else if memory.extra.rng.is_none() {
+            err!(ReadBytesAsPointer)
+        } else {
+           intptrcast::GlobalState::int_to_ptr(int, memory)
+        }
+    }
+    fn ptr_to_int(
+        ptr: Pointer<Self::PointerTag>,
+        memory: &Memory<'mir, 'tcx, Self>,
+    ) -> InterpResult<'tcx, u64> {
+        if memory.extra.rng.is_none() {
+            err!(ReadPointerAsBytes)
+        } else {
+            intptrcast::GlobalState::ptr_to_int(ptr, memory)
+        }
     }
 }
diff --git a/src/memory.rs b/src/memory.rs
new file mode 100644 (file)
index 0000000..ea8f01a
--- /dev/null
@@ -0,0 +1,51 @@
+use rand::rngs::StdRng;
+
+use rustc_mir::interpret::{Pointer, Allocation, AllocationExtra, InterpResult};
+use rustc_target::abi::Size;
+
+use crate::{stacked_borrows, intptrcast};
+use crate::stacked_borrows::Tag;
+
+#[derive(Default, Clone, Debug)]
+pub struct MemoryExtra {
+    pub stacked_borrows: stacked_borrows::MemoryExtra,
+    pub intptrcast: intptrcast::MemoryExtra,
+    /// The random number generator to use if Miri is running in non-deterministic mode and to
+    /// enable intptrcast
+    pub(crate) rng: Option<StdRng>
+}
+
+#[derive(Debug, Clone)]
+pub struct AllocExtra {
+    pub stacked_borrows: stacked_borrows::AllocExtra,
+    pub intptrcast: intptrcast::AllocExtra,
+}
+
+impl AllocationExtra<Tag> for AllocExtra {
+    #[inline(always)]
+    fn memory_read<'tcx>(
+        alloc: &Allocation<Tag, AllocExtra>,
+        ptr: Pointer<Tag>,
+        size: Size,
+    ) -> InterpResult<'tcx> {
+        alloc.extra.stacked_borrows.memory_read(ptr, size)
+    }
+
+    #[inline(always)]
+    fn memory_written<'tcx>(
+        alloc: &mut Allocation<Tag, AllocExtra>,
+        ptr: Pointer<Tag>,
+        size: Size,
+    ) -> InterpResult<'tcx> {
+        alloc.extra.stacked_borrows.memory_written(ptr, size)
+    }
+
+    #[inline(always)]
+    fn memory_deallocated<'tcx>(
+        alloc: &mut Allocation<Tag, AllocExtra>,
+        ptr: Pointer<Tag>,
+        size: Size,
+    ) -> InterpResult<'tcx> {
+        alloc.extra.stacked_borrows.memory_deallocated(ptr, size)
+    }
+}
index cb258cf150d750d6cf36871c222514400aa9553a..572794a44add4604126d6929f6129a5c7647092c 100644 (file)
@@ -56,6 +56,19 @@ fn ptr_op(
 
         trace!("ptr_op: {:?} {:?} {:?}", *left, bin_op, *right);
 
+        // If intptrcast is enabled and the operation is not an offset
+        // we can force the cast from pointers to integer addresses and
+        // then dispatch to rustc binary operation method
+        if self.memory().extra.rng.is_some() && bin_op != Offset {
+            let l_bits = self.force_bits(left.imm.to_scalar()?, left.layout.size)?;
+            let r_bits = self.force_bits(right.imm.to_scalar()?, right.layout.size)?;
+            
+            let left = ImmTy::from_scalar(Scalar::from_uint(l_bits, left.layout.size), left.layout);
+            let right = ImmTy::from_scalar(Scalar::from_uint(r_bits, left.layout.size), right.layout);
+
+            return self.binary_op(bin_op, left, right);
+        } 
+
         // Operations that support fat pointers
         match bin_op {
             Eq | Ne => {
index 3a0c257428e11c5b62de1242eb01795e2e24e734..524ad2b47af0e0147a1fd7254d70aeb2ccdbf4fe 100644 (file)
 
 use crate::{
     InterpResult, InterpError, MiriEvalContext, HelpersEvalContextExt, Evaluator, MutValueVisitor,
-    MemoryKind, MiriMemoryKind, RangeMap, Allocation, AllocationExtra, AllocId,
-    Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
+    MemoryKind, MiriMemoryKind, RangeMap, AllocId, Pointer, Immediate, ImmTy, PlaceTy, MPlaceTy,
 };
 
 pub type PtrId = NonZeroU64;
 pub type CallId = NonZeroU64;
+pub type AllocExtra = Stacks;
 
 /// Tracking pointer provenance
 #[derive(Copy, Clone, Hash, PartialEq, Eq)]
@@ -86,7 +86,7 @@ pub struct Stacks {
     // Even reading memory can have effects on the stack, so we need a `RefCell` here.
     stacks: RefCell<RangeMap<Stack>>,
     // Pointer to global state
-    global: MemoryState,
+    global: MemoryExtra,
 }
 
 /// Extra global state, available to the memory access hooks.
@@ -104,7 +104,7 @@ pub struct GlobalState {
     active_calls: HashSet<CallId>,
 }
 /// Memory extra state gives us interior mutable access to the global state.
-pub type MemoryState = Rc<RefCell<GlobalState>>;
+pub type MemoryExtra = Rc<RefCell<GlobalState>>;
 
 /// Indicates which kind of access is being performed.
 #[derive(Copy, Clone, Hash, PartialEq, Eq)]
@@ -423,15 +423,16 @@ fn new(
         size: Size,
         perm: Permission,
         tag: Tag,
-        extra: MemoryState,
+        extra: MemoryExtra,
     ) -> Self {
         let item = Item { perm, tag, protector: None };
         let stack = Stack {
             borrows: vec![item],
         };
+
         Stacks {
             stacks: RefCell::new(RangeMap::new(size, stack)),
-            global: extra,
+            global: extra, 
         }
     }
 
@@ -456,7 +457,7 @@ impl Stacks {
     pub fn new_allocation(
         id: AllocId,
         size: Size,
-        extra: MemoryState,
+        extra: MemoryExtra, 
         kind: MemoryKind<MiriMemoryKind>,
     ) -> (Self, Tag) {
         let (tag, perm) = match kind {
@@ -475,43 +476,41 @@ pub fn new_allocation(
         let stack = Stacks::new(size, perm, tag, extra);
         (stack, tag)
     }
-}
 
-impl AllocationExtra<Tag> for Stacks {
     #[inline(always)]
-    fn memory_read<'tcx>(
-        alloc: &Allocation<Tag, Stacks>,
+    pub fn memory_read<'tcx>(
+        &self,
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
         trace!("read access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes());
-        alloc.extra.for_each(ptr, size, |stack, global| {
+        self.for_each(ptr, size, |stack, global| {
             stack.access(AccessKind::Read, ptr.tag, global)?;
             Ok(())
         })
     }
 
     #[inline(always)]
-    fn memory_written<'tcx>(
-        alloc: &mut Allocation<Tag, Stacks>,
+    pub fn memory_written<'tcx>(
+        &mut self,
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
         trace!("write access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes());
-        alloc.extra.for_each(ptr, size, |stack, global| {
+        self.for_each(ptr, size, |stack, global| {
             stack.access(AccessKind::Write, ptr.tag, global)?;
             Ok(())
         })
     }
 
     #[inline(always)]
-    fn memory_deallocated<'tcx>(
-        alloc: &mut Allocation<Tag, Stacks>,
+    pub fn memory_deallocated<'tcx>(
+        &mut self,
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
         trace!("deallocation with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes());
-        alloc.extra.for_each(ptr, size, |stack, global| {
+        self.for_each(ptr, size, |stack, global| {
             stack.dealloc(ptr.tag, global)
         })
     }
@@ -554,14 +553,14 @@ fn reborrow(
                     // We are only ever `SharedReadOnly` inside the frozen bits.
                     let perm = if frozen { Permission::SharedReadOnly } else { Permission::SharedReadWrite };
                     let item = Item { perm, tag: new_tag, protector };
-                    alloc.extra.for_each(cur_ptr, size, |stack, global| {
+                    alloc.extra.stacked_borrows.for_each(cur_ptr, size, |stack, global| {
                         stack.grant(cur_ptr.tag, item, global)
                     })
                 });
             }
         };
         let item = Item { perm, tag: new_tag, protector };
-        alloc.extra.for_each(ptr, size, |stack, global| {
+        alloc.extra.stacked_borrows.for_each(ptr, size, |stack, global| {
             stack.grant(ptr.tag, item, global)
         })
     }
@@ -588,7 +587,7 @@ fn retag_reference(
         // Compute new borrow.
         let new_tag = match kind {
             RefKind::Raw { .. } => Tag::Untagged,
-            _ => Tag::Tagged(this.memory().extra.borrow_mut().new_ptr()),
+            _ => Tag::Tagged(this.memory().extra.stacked_borrows.borrow_mut().new_ptr()),
         };
 
         // Reborrow.
diff --git a/tests/compile-fail/intptrcast_cast_int_to_fn_ptr.rs b/tests/compile-fail/intptrcast_cast_int_to_fn_ptr.rs
new file mode 100644 (file)
index 0000000..f4064cf
--- /dev/null
@@ -0,0 +1,10 @@
+// Validation makes this fail in the wrong place
+// compile-flags: -Zmiri-disable-validation -Zmiri-seed=0000000000000000
+
+fn main() {
+    let g = unsafe {
+        std::mem::transmute::<usize, fn(i32)>(42)
+    };
+
+    g(42) //~ ERROR dangling pointer was dereferenced
+}
diff --git a/tests/compile-fail/intptrcast_null_pointer_deref.rs b/tests/compile-fail/intptrcast_null_pointer_deref.rs
new file mode 100644 (file)
index 0000000..0c5d466
--- /dev/null
@@ -0,0 +1,6 @@
+// compile-flags: -Zmiri-seed=0000000000000000
+
+fn main() {
+    let x: i32 = unsafe { *std::ptr::null() }; //~ ERROR invalid use of NULL pointer
+    panic!("this should never print: {}", x);
+}
diff --git a/tests/compile-fail/intptrcast_wild_pointer_deref.rs b/tests/compile-fail/intptrcast_wild_pointer_deref.rs
new file mode 100644 (file)
index 0000000..2ee664e
--- /dev/null
@@ -0,0 +1,7 @@
+// compile-flags: -Zmiri-seed=0000000000000000
+
+fn main() {
+    let p = 44 as *const i32;
+    let x = unsafe { *p }; //~ ERROR dangling pointer was dereferenced
+    panic!("this should never print: {}", x);
+}
diff --git a/tests/run-pass/intptrcast.rs b/tests/run-pass/intptrcast.rs
new file mode 100644 (file)
index 0000000..4ff57ca
--- /dev/null
@@ -0,0 +1,9 @@
+// compile-flags: -Zmiri-seed=0000000000000000
+
+fn main() {
+    let x = &42 as *const i32 as usize; 
+    let y = x * 2;
+    assert_eq!(y, x + x);
+    let z = y as u8 as usize;
+    assert_eq!(z, y % 256);
+}