1
Fork 0

align allocations in the worst possible way

This commit is contained in:
Oliver Schneider 2016-07-05 14:27:27 +02:00
parent 4781a6ba54
commit 082effb3ee
No known key found for this signature in database
GPG key ID: 56D6EEA0FC67AC46
8 changed files with 71 additions and 39 deletions

View file

@ -36,6 +36,10 @@ pub enum EvalError<'tcx> {
},
ExecutionTimeLimitReached,
StackFrameLimitReached,
AlignmentCheckFailed {
required: usize,
has: usize,
},
}
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
@ -82,6 +86,8 @@ impl<'tcx> Error for EvalError<'tcx> {
"reached the configured maximum execution time",
EvalError::StackFrameLimitReached =>
"reached the configured maximum number of stack frames",
EvalError::AlignmentCheckFailed{..} =>
"tried to execute a misaligned read or write",
}
}
@ -106,6 +112,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
allocation_size, memory_size - memory_usage, memory_size),
EvalError::AlignmentCheckFailed { required, has } =>
write!(f, "tried to access memory with alignment {}, but alignment {} is required",
has, required),
_ => write!(f, "{}", self.description()),
}
}

View file

@ -153,7 +153,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match output_ty {
ty::FnConverging(ty) => {
let size = self.type_size_with_substs(ty, substs);
self.memory.allocate(size).map(Some)
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align).map(Some)
}
ty::FnDiverging => Ok(None),
}
@ -177,7 +178,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
macro_rules! i2p {
($i:ident, $n:expr) => {{
let ptr = self.memory.allocate($n)?;
let ptr = self.memory.allocate($n, $n)?;
self.memory.write_int(ptr, $i as i64, $n)?;
Ok(ptr)
}}
@ -202,8 +203,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8),
Str(ref s) => {
let psize = self.memory.pointer_size();
let static_ptr = self.memory.allocate(s.len())?;
let ptr = self.memory.allocate(psize * 2)?;
let static_ptr = self.memory.allocate(s.len(), 1)?;
let ptr = self.memory.allocate(psize * 2, psize)?;
self.memory.write_bytes(static_ptr, s.as_bytes())?;
self.memory.write_ptr(ptr, static_ptr)?;
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
@ -211,19 +212,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
ByteStr(ref bs) => {
let psize = self.memory.pointer_size();
let static_ptr = self.memory.allocate(bs.len())?;
let ptr = self.memory.allocate(psize)?;
let static_ptr = self.memory.allocate(bs.len(), 1)?;
let ptr = self.memory.allocate(psize, psize)?;
self.memory.write_bytes(static_ptr, bs)?;
self.memory.write_ptr(ptr, static_ptr)?;
Ok(ptr)
}
Bool(b) => {
let ptr = self.memory.allocate(1)?;
let ptr = self.memory.allocate(1, 1)?;
self.memory.write_bool(ptr, b)?;
Ok(ptr)
}
Char(c) => {
let ptr = self.memory.allocate(4)?;
let ptr = self.memory.allocate(4, 4)?;
self.memory.write_uint(ptr, c as u64, 4)?;
Ok(ptr)
},
@ -269,10 +270,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.type_size_with_substs(ty, self.substs())
}
fn type_align(&self, ty: Ty<'tcx>) -> usize {
self.type_align_with_substs(ty, self.substs())
}
fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize {
self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize
}
fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize {
self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).pref() as usize
}
fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout {
self.type_layout_with_substs(ty, self.substs())
}
@ -306,7 +315,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let locals: EvalResult<'tcx, Vec<Pointer>> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
let size = self.type_size_with_substs(ty, substs);
self.memory.allocate(size)
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align)
}).collect();
self.stack.push(Frame {
@ -553,7 +563,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Box(ty) => {
let size = self.type_size(ty);
let ptr = self.memory.allocate(size)?;
let align = self.type_align(ty);
let ptr = self.memory.allocate(size, align)?;
self.memory.write_ptr(dest, ptr)?;
}
@ -701,7 +712,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Item { def_id, substs } => {
if let ty::TyFnDef(..) = ty.sty {
// function items are zero sized
Ok(self.memory.allocate(0)?)
Ok(self.memory.allocate(0, 0)?)
} else {
let cid = ConstantId {
def_id: def_id,
@ -955,9 +966,9 @@ pub fn eval_main<'a, 'tcx: 'a>(
if mir.arg_decls.len() == 2 {
// start function
let ptr_size = ecx.memory().pointer_size();
let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs");
let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs");
ecx.memory_mut().write_usize(nargs, 0).unwrap();
let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer");
let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer");
ecx.memory_mut().write_usize(args, 0).unwrap();
ecx.frame_mut().locals[0] = nargs;
ecx.frame_mut().locals[1] = args;

View file

@ -88,7 +88,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
let ptr = self.eval_operand(func)?;
assert_eq!(ptr.offset, 0);
let fn_ptr = self.memory.read_ptr(ptr)?;
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
if fn_ty != bare_fn_ty {
@ -416,14 +415,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match &link_name[..] {
"__rust_allocate" => {
let size = self.memory.read_usize(args[0])?;
let ptr = self.memory.allocate(size as usize)?;
let align = self.memory.read_usize(args[1])?;
let ptr = self.memory.allocate(size as usize, align as usize)?;
self.memory.write_ptr(dest, ptr)?;
}
"__rust_reallocate" => {
let ptr = self.memory.read_ptr(args[0])?;
let size = self.memory.read_usize(args[2])?;
let new_ptr = self.memory.reallocate(ptr, size as usize)?;
let align = self.memory.read_usize(args[3])?;
let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?;
self.memory.write_ptr(dest, new_ptr)?;
}

View file

@ -29,6 +29,7 @@ pub struct Allocation {
pub bytes: Vec<u8>,
pub relocations: BTreeMap<usize, AllocId>,
pub undef_mask: UndefMask,
pub align: usize,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -98,10 +99,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
bytes: Vec::new(),
relocations: BTreeMap::new(),
undef_mask: UndefMask::new(0),
align: 0,
};
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
// check that additional zst allocs work
debug_assert!(mem.allocate(0).unwrap().points_to_zst());
debug_assert!(mem.allocate(0, 0).unwrap().points_to_zst());
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
mem
}
@ -133,62 +135,71 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
}
pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> {
pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
if size == 0 {
return Ok(Pointer::zst_ptr());
}
// make sure we can offset the result pointer by the worst possible alignment
// this allows cheaply checking for alignment directly in the pointer
let least_aligned_size = size + align;
if self.memory_size - self.memory_usage < size as u64 {
return Err(EvalError::OutOfMemory {
allocation_size: size as u64,
allocation_size: least_aligned_size as u64,
memory_size: self.memory_size,
memory_usage: self.memory_usage,
});
}
self.memory_usage += size as u64;
let alloc = Allocation {
bytes: vec![0; size],
bytes: vec![0; least_aligned_size],
relocations: BTreeMap::new(),
undef_mask: UndefMask::new(size),
undef_mask: UndefMask::new(least_aligned_size),
align: align,
};
let id = self.next_id;
self.next_id.0 += 1;
self.alloc_map.insert(id, alloc);
Ok(Pointer {
alloc_id: id,
offset: 0,
// offset by the alignment, so larger accesses will fail
offset: align,
})
}
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
// when reallocating/deallocating any others.
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> {
if ptr.offset != 0 {
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
if ptr.offset != self.get(ptr.alloc_id)?.align {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
}
if ptr.points_to_zst() {
return self.allocate(new_size);
return self.allocate(new_size, align);
}
let size = self.get_mut(ptr.alloc_id)?.bytes.len();
let size = self.get(ptr.alloc_id)?.bytes.len();
let least_aligned_size = new_size + align;
if new_size > size {
let amount = new_size - size;
if least_aligned_size > size {
let amount = least_aligned_size - size;
self.memory_usage += amount as u64;
let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.extend(iter::repeat(0).take(amount));
alloc.undef_mask.grow(amount, false);
} else if size > new_size {
} else if size > least_aligned_size {
// it's possible to cause miri to use arbitrary amounts of memory that aren't detectable
// through the memory_usage value, by allocating a lot and reallocating to zero
self.memory_usage -= (size - new_size) as u64;
self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?;
self.memory_usage -= (size - least_aligned_size) as u64;
self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?;
let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.truncate(new_size);
alloc.undef_mask.truncate(new_size);
alloc.bytes.truncate(least_aligned_size);
alloc.undef_mask.truncate(least_aligned_size);
}
Ok(ptr)
Ok(Pointer {
alloc_id: ptr.alloc_id,
offset: align,
})
}
// TODO(solson): See comment on `reallocate`.
@ -196,7 +207,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
if ptr.points_to_zst() {
return Ok(());
}
if ptr.offset != 0 {
if ptr.offset != self.get(ptr.alloc_id)?.align {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
}

View file

@ -6,6 +6,6 @@ fn bar() {
assert_eq!(x, 6);
}
fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory
fn main() { //~ ERROR tried to allocate 8 more bytes, but only 0 bytes are free of the 0 byte memory
bar();
}

View file

@ -3,7 +3,7 @@
fn bar(i: i32) {
if i < 1000 {
bar(i + 1) //~ ERROR tried to allocate 4 more bytes, but only 1 bytes are free of the 1000 byte memory
bar(i + 1) //~ ERROR tried to allocate 8 more bytes, but only 1 bytes are free of the 1000 byte memory
//~^NOTE inside call to bar
//~|NOTE inside call to bar
//~|NOTE inside call to bar

View file

@ -1,5 +1,5 @@
fn main() {
let v: Vec<u8> = vec![1, 2];
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 2
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: which has size 3
panic!("this should never print: {}", x);
}

View file

@ -1,5 +1,5 @@
fn main() {
let v: Vec<u8> = vec![1, 2];
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 5..6 outside bounds of allocation
let x = unsafe { *v.get_unchecked(5) }; //~ ERROR: memory access of 6..7 outside bounds of allocation
panic!("this should never print: {}", x);
}