From 875a4542f9eeace01f1ee5ec5fd25904601f58c5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 22 Sep 2016 15:22:00 +0200 Subject: [PATCH] remove the ZST allocation and abort all zero byte writes/reads --- src/error.rs | 3 +++ src/memory.rs | 45 +++++++++++++++++++++++---------------- tests/compile-fail/zst.rs | 4 ++++ 3 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 tests/compile-fail/zst.rs diff --git a/src/error.rs b/src/error.rs index 5624734e888..82b4eff6e92 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,6 +10,7 @@ use syntax::codemap::Span; pub enum EvalError<'tcx> { FunctionPointerTyMismatch(&'tcx BareFnTy<'tcx>, &'tcx BareFnTy<'tcx>), DanglingPointerDeref, + ZstAllocAccess, InvalidFunctionPointer, InvalidBool, InvalidDiscriminant, @@ -53,6 +54,8 @@ impl<'tcx> Error for EvalError<'tcx> { match *self { EvalError::FunctionPointerTyMismatch(..) => "tried to call a function through a function pointer of a different type", + EvalError::ZstAllocAccess => + "tried to access the ZST allocation", EvalError::DanglingPointerDeref => "dangling pointer was dereferenced", EvalError::InvalidFunctionPointer => diff --git a/src/memory.rs b/src/memory.rs index ae590b0eef1..53c50e45e85 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -105,7 +105,7 @@ const ZST_ALLOC_ID: AllocId = AllocId(0); impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self { - let mut mem = Memory { + Memory { alloc_map: HashMap::new(), functions: HashMap::new(), function_alloc_cache: HashMap::new(), @@ -113,21 +113,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { layout: layout, memory_size: max_memory, memory_usage: 0, - }; - // alloc id 0 is reserved for ZSTs, this is an optimization to prevent ZST - // (e.g. function items, (), [], ...) from requiring memory - let alloc = Allocation { - bytes: Vec::new(), - relocations: BTreeMap::new(), - undef_mask: UndefMask::new(0), - align: 8, // should be infinity? - immutable: false, // must be mutable, because sometimes we "move out" of a ZST - }; - mem.alloc_map.insert(ZST_ALLOC_ID, alloc); - // check that additional zst allocs work - debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst()); - debug_assert!(mem.get(ZST_ALLOC_ID).is_ok()); - mem + } } pub fn allocations(&self) -> ::std::collections::hash_map::Iter { @@ -293,6 +279,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), + None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -304,6 +291,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { Some(alloc) => Ok(alloc), None => match self.functions.get(&id) { Some(_) => Err(EvalError::DerefFunctionPointer), + None if id == ZST_ALLOC_ID => Err(EvalError::ZstAllocAccess), None => Err(EvalError::DanglingPointerDeref), } } @@ -353,6 +341,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { while let Some(id) = allocs_to_print.pop_front() { allocs_seen.insert(id); let mut msg = format!("Alloc {:<5} ", format!("{}:", id)); + if id == ZST_ALLOC_ID { + trace!("{} zst allocation", msg); + continue; + } let prefix_len = msg.len(); let mut relocations = vec![]; @@ -406,6 +398,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Byte accessors impl<'a, 'tcx> Memory<'a, 'tcx> { fn get_bytes_unchecked(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> { + if size == 0 { + return Ok(&[]); + } let alloc = self.get(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -418,6 +413,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes_unchecked_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> { + if size == 0 { + return Ok(&mut []); + } let alloc = self.get_mut(ptr.alloc_id)?; if ptr.offset + size > alloc.bytes.len() { return Err(EvalError::PointerOutOfBounds { @@ -430,6 +428,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> { + if size == 0 { + return Ok(&[]); + } self.check_align(ptr, align)?; if self.relocations(ptr, size)?.count() != 0 { return Err(EvalError::ReadPointerAsBytes); @@ -439,6 +440,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> { + if size == 0 { + return Ok(&mut []); + } self.check_align(ptr, align)?; self.clear_relocations(ptr, size)?; self.mark_definedness(ptr, size, true)?; @@ -449,8 +453,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { /// Reading and writing impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn freeze(&mut self, alloc_id: AllocId) -> EvalResult<'tcx, ()> { - // Never freeze the zero-sized allocation. If you do that, then getting a mutable handle to - // _any_ ZST becomes an error, since they all share the same allocation. + // It's not possible to freeze the zero-sized allocation, because it doesn't exist. if alloc_id != ZST_ALLOC_ID { self.get_mut(alloc_id)?.immutable = true; } @@ -458,6 +461,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> { + if size == 0 { + return Ok(()); + } self.check_relocation_edges(src, size)?; let src_bytes = self.get_bytes_unchecked(src, size)?.as_ptr(); @@ -714,6 +720,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { pub fn mark_definedness(&mut self, ptr: Pointer, size: usize, new_state: bool) -> EvalResult<'tcx, ()> { + if size == 0 { + return Ok(()) + } let mut alloc = self.get_mut(ptr.alloc_id)?; alloc.undef_mask.set_range(ptr.offset, ptr.offset + size, new_state); Ok(()) diff --git a/tests/compile-fail/zst.rs b/tests/compile-fail/zst.rs new file mode 100644 index 00000000000..a244befed01 --- /dev/null +++ b/tests/compile-fail/zst.rs @@ -0,0 +1,4 @@ +fn main() { + let x = &() as *const () as *const i32; + let _ = unsafe { *x }; //~ ERROR: tried to access the ZST allocation +}