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, ExecutionTimeLimitReached,
StackFrameLimitReached, StackFrameLimitReached,
AlignmentCheckFailed {
required: usize,
has: usize,
},
} }
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>; 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", "reached the configured maximum execution time",
EvalError::StackFrameLimitReached => EvalError::StackFrameLimitReached =>
"reached the configured maximum number of stack frames", "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 } => EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory", write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
allocation_size, memory_size - memory_usage, memory_size), 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()), _ => write!(f, "{}", self.description()),
} }
} }

View file

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

View file

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

View file

@ -29,6 +29,7 @@ pub struct Allocation {
pub bytes: Vec<u8>, pub bytes: Vec<u8>,
pub relocations: BTreeMap<usize, AllocId>, pub relocations: BTreeMap<usize, AllocId>,
pub undef_mask: UndefMask, pub undef_mask: UndefMask,
pub align: usize,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -98,10 +99,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
bytes: Vec::new(), bytes: Vec::new(),
relocations: BTreeMap::new(), relocations: BTreeMap::new(),
undef_mask: UndefMask::new(0), undef_mask: UndefMask::new(0),
align: 0,
}; };
mem.alloc_map.insert(ZST_ALLOC_ID, alloc); mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
// check that additional zst allocs work // 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()); debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
mem 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 { if size == 0 {
return Ok(Pointer::zst_ptr()); 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 { if self.memory_size - self.memory_usage < size as u64 {
return Err(EvalError::OutOfMemory { return Err(EvalError::OutOfMemory {
allocation_size: size as u64, allocation_size: least_aligned_size as u64,
memory_size: self.memory_size, memory_size: self.memory_size,
memory_usage: self.memory_usage, memory_usage: self.memory_usage,
}); });
} }
self.memory_usage += size as u64; self.memory_usage += size as u64;
let alloc = Allocation { let alloc = Allocation {
bytes: vec![0; size], bytes: vec![0; least_aligned_size],
relocations: BTreeMap::new(), relocations: BTreeMap::new(),
undef_mask: UndefMask::new(size), undef_mask: UndefMask::new(least_aligned_size),
align: align,
}; };
let id = self.next_id; let id = self.next_id;
self.next_id.0 += 1; self.next_id.0 += 1;
self.alloc_map.insert(id, alloc); self.alloc_map.insert(id, alloc);
Ok(Pointer { Ok(Pointer {
alloc_id: id, 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 // TODO(solson): Track which allocations were returned from __rust_allocate and report an error
// when reallocating/deallocating any others. // when reallocating/deallocating any others.
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> { pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
if ptr.offset != 0 { if ptr.offset != self.get(ptr.alloc_id)?.align {
// TODO(solson): Report error about non-__rust_allocate'd pointer. // TODO(solson): Report error about non-__rust_allocate'd pointer.
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
} }
if ptr.points_to_zst() { 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 { if least_aligned_size > size {
let amount = new_size - size; let amount = least_aligned_size - size;
self.memory_usage += amount as u64; self.memory_usage += amount as u64;
let alloc = self.get_mut(ptr.alloc_id)?; let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.extend(iter::repeat(0).take(amount)); alloc.bytes.extend(iter::repeat(0).take(amount));
alloc.undef_mask.grow(amount, false); 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 // 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 // through the memory_usage value, by allocating a lot and reallocating to zero
self.memory_usage -= (size - new_size) as u64; self.memory_usage -= (size - least_aligned_size) as u64;
self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?; self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?;
let alloc = self.get_mut(ptr.alloc_id)?; let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.truncate(new_size); alloc.bytes.truncate(least_aligned_size);
alloc.undef_mask.truncate(new_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`. // TODO(solson): See comment on `reallocate`.
@ -196,7 +207,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
if ptr.points_to_zst() { if ptr.points_to_zst() {
return Ok(()); 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. // TODO(solson): Report error about non-__rust_allocate'd pointer.
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset))); return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
} }

View file

@ -6,6 +6,6 @@ fn bar() {
assert_eq!(x, 6); 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(); bar();
} }

View file

@ -3,7 +3,7 @@
fn bar(i: i32) { fn bar(i: i32) {
if i < 1000 { 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 //~|NOTE inside call to bar
//~|NOTE inside call to bar //~|NOTE inside call to bar

View file

@ -1,5 +1,5 @@
fn main() { fn main() {
let v: Vec<u8> = vec![1, 2]; 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); panic!("this should never print: {}", x);
} }

View file

@ -1,5 +1,5 @@
fn main() { fn main() {
let v: Vec<u8> = vec![1, 2]; 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); panic!("this should never print: {}", x);
} }