Merge pull request #43 from oli-obk/the_outer_limits
limit stack size, memory size and execution time
This commit is contained in:
commit
0948e2dc9c
10 changed files with 240 additions and 52 deletions
|
@ -13,6 +13,7 @@ use miri::{eval_main, run_mir_passes};
|
|||
use rustc::session::Session;
|
||||
use rustc::mir::mir_map::MirMap;
|
||||
use rustc_driver::{driver, CompilerCalls, Compilation};
|
||||
use syntax::ast::MetaItemKind;
|
||||
|
||||
struct MiriCompilerCalls;
|
||||
|
||||
|
@ -23,7 +24,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
|
|||
_: &getopts::Matches
|
||||
) -> driver::CompileController<'a> {
|
||||
let mut control = driver::CompileController::basic();
|
||||
|
||||
control.after_hir_lowering.callback = Box::new(|state| {
|
||||
state.session.plugin_attributes.borrow_mut().push(("miri".to_owned(), syntax::feature_gate::AttributeType::Whitelisted));
|
||||
});
|
||||
control.after_analysis.stop = Compilation::Stop;
|
||||
control.after_analysis.callback = Box::new(|state| {
|
||||
state.session.abort_if_errors();
|
||||
|
@ -33,9 +36,39 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
|
|||
let (node_id, _) = state.session.entry_fn.borrow()
|
||||
.expect("no main or start function found");
|
||||
|
||||
let krate = state.hir_crate.as_ref().unwrap();
|
||||
let mut memory_size = 100*1024*1024; // 100MB
|
||||
let mut step_limit = 1000_000;
|
||||
let mut stack_limit = 100;
|
||||
fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString {
|
||||
match lit.node {
|
||||
syntax::ast::LitKind::Str(ref s, _) => s.clone(),
|
||||
_ => panic!("attribute values need to be strings"),
|
||||
}
|
||||
}
|
||||
for attr in krate.attrs.iter() {
|
||||
match attr.node.value.node {
|
||||
MetaItemKind::List(ref name, _) if name != "miri" => {}
|
||||
MetaItemKind::List(_, ref items) => for item in items {
|
||||
match item.node {
|
||||
MetaItemKind::NameValue(ref name, ref value) => {
|
||||
match &**name {
|
||||
"memory_size" => memory_size = extract_str(value).parse().expect("not a number"),
|
||||
"step_limit" => step_limit = extract_str(value).parse().expect("not a number"),
|
||||
"stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"),
|
||||
_ => state.session.span_err(item.span, "unknown miri attribute"),
|
||||
}
|
||||
}
|
||||
_ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"),
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
let mut mir_map = MirMap { map: mir_map.map.clone() };
|
||||
run_mir_passes(tcx, &mut mir_map);
|
||||
eval_main(tcx, &mir_map, node_id);
|
||||
eval_main(tcx, &mir_map, node_id, memory_size, step_limit, stack_limit);
|
||||
|
||||
state.session.abort_if_errors();
|
||||
});
|
||||
|
|
16
src/error.rs
16
src/error.rs
|
@ -29,6 +29,13 @@ pub enum EvalError<'tcx> {
|
|||
ArrayIndexOutOfBounds(Span, u64, u64),
|
||||
Math(Span, ConstMathErr),
|
||||
InvalidChar(u32),
|
||||
OutOfMemory {
|
||||
allocation_size: usize,
|
||||
memory_size: usize,
|
||||
memory_usage: usize,
|
||||
},
|
||||
ExecutionTimeLimitReached,
|
||||
StackFrameLimitReached,
|
||||
}
|
||||
|
||||
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
|
||||
|
@ -69,6 +76,12 @@ impl<'tcx> Error for EvalError<'tcx> {
|
|||
"mathematical operation failed",
|
||||
EvalError::InvalidChar(..) =>
|
||||
"tried to interpret an invalid 32-bit value as a char",
|
||||
EvalError::OutOfMemory{..} =>
|
||||
"could not allocate more memory",
|
||||
EvalError::ExecutionTimeLimitReached =>
|
||||
"reached the configured maximum execution time",
|
||||
EvalError::StackFrameLimitReached =>
|
||||
"reached the configured maximum number of stack frames",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +103,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
|
|||
write!(f, "{:?} at {:?}", err, span),
|
||||
EvalError::InvalidChar(c) =>
|
||||
write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
|
||||
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),
|
||||
_ => write!(f, "{}", self.description()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,9 @@ pub struct EvalContext<'a, 'tcx: 'a> {
|
|||
|
||||
/// The virtual call stack.
|
||||
stack: Vec<Frame<'a, 'tcx>>,
|
||||
|
||||
/// The maximum number of stack frames allowed
|
||||
stack_limit: usize,
|
||||
}
|
||||
|
||||
/// A stack frame.
|
||||
|
@ -133,24 +136,25 @@ enum ConstantKind {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self {
|
||||
EvalContext {
|
||||
tcx: tcx,
|
||||
mir_map: mir_map,
|
||||
mir_cache: RefCell::new(DefIdMap()),
|
||||
memory: Memory::new(&tcx.data_layout),
|
||||
memory: Memory::new(&tcx.data_layout, memory_size),
|
||||
statics: HashMap::new(),
|
||||
stack: Vec::new(),
|
||||
stack_limit: stack_limit,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option<Pointer> {
|
||||
pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option<Pointer>> {
|
||||
match output_ty {
|
||||
ty::FnConverging(ty) => {
|
||||
let size = self.type_size_with_substs(ty, substs);
|
||||
Some(self.memory.allocate(size))
|
||||
self.memory.allocate(size).map(Some)
|
||||
}
|
||||
ty::FnDiverging => None,
|
||||
ty::FnDiverging => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,19 +176,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
|
||||
macro_rules! i2p {
|
||||
($i:ident, $n:expr) => {{
|
||||
let ptr = self.memory.allocate($n);
|
||||
let ptr = self.memory.allocate($n)?;
|
||||
self.memory.write_int(ptr, $i as i64, $n)?;
|
||||
Ok(ptr)
|
||||
}}
|
||||
}
|
||||
match *const_val {
|
||||
Float(ConstFloat::F32(f)) => {
|
||||
let ptr = self.memory.allocate(4);
|
||||
let ptr = self.memory.allocate(4)?;
|
||||
self.memory.write_f32(ptr, f)?;
|
||||
Ok(ptr)
|
||||
},
|
||||
Float(ConstFloat::F64(f)) => {
|
||||
let ptr = self.memory.allocate(8);
|
||||
let ptr = self.memory.allocate(8)?;
|
||||
self.memory.write_f64(ptr, f)?;
|
||||
Ok(ptr)
|
||||
},
|
||||
|
@ -207,8 +211,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())?;
|
||||
let ptr = self.memory.allocate(psize * 2)?;
|
||||
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)?;
|
||||
|
@ -216,19 +220,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())?;
|
||||
let ptr = self.memory.allocate(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)?;
|
||||
self.memory.write_bool(ptr, b)?;
|
||||
Ok(ptr)
|
||||
}
|
||||
Char(c) => {
|
||||
let ptr = self.memory.allocate(4);
|
||||
let ptr = self.memory.allocate(4)?;
|
||||
self.memory.write_uint(ptr, c as u64, 4)?;
|
||||
Ok(ptr)
|
||||
},
|
||||
|
@ -292,9 +296,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>,
|
||||
return_ptr: Option<Pointer>)
|
||||
{
|
||||
pub fn push_stack_frame(
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
span: codemap::Span,
|
||||
mir: CachedMir<'a, 'tcx>,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
return_ptr: Option<Pointer>,
|
||||
) -> EvalResult<'tcx, ()> {
|
||||
let arg_tys = mir.arg_decls.iter().map(|a| a.ty);
|
||||
let var_tys = mir.var_decls.iter().map(|v| v.ty);
|
||||
let temp_tys = mir.temp_decls.iter().map(|t| t.ty);
|
||||
|
@ -304,7 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
::log_settings::settings().indentation += 1;
|
||||
|
||||
let locals: 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);
|
||||
self.memory.allocate(size)
|
||||
}).collect();
|
||||
|
@ -313,7 +322,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
mir: mir.clone(),
|
||||
block: mir::START_BLOCK,
|
||||
return_ptr: return_ptr,
|
||||
locals: locals,
|
||||
locals: locals?,
|
||||
var_offset: num_args,
|
||||
temp_offset: num_args + num_vars,
|
||||
span: span,
|
||||
|
@ -321,6 +330,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
substs: substs,
|
||||
stmt: 0,
|
||||
});
|
||||
if self.stack.len() > self.stack_limit {
|
||||
Err(EvalError::StackFrameLimitReached)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_stack_frame(&mut self) {
|
||||
|
@ -548,7 +562,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
Box(ty) => {
|
||||
let size = self.type_size(ty);
|
||||
let ptr = self.memory.allocate(size);
|
||||
let ptr = self.memory.allocate(size)?;
|
||||
self.memory.write_ptr(dest, ptr)?;
|
||||
}
|
||||
|
||||
|
@ -696,7 +710,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)?)
|
||||
} else {
|
||||
let cid = ConstantId {
|
||||
def_id: def_id,
|
||||
|
@ -935,37 +949,44 @@ pub fn eval_main<'a, 'tcx: 'a>(
|
|||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
mir_map: &'a MirMap<'tcx>,
|
||||
node_id: ast::NodeId,
|
||||
memory_size: usize,
|
||||
step_limit: u64,
|
||||
stack_limit: usize,
|
||||
) {
|
||||
let mir = mir_map.map.get(&node_id).expect("no mir for main function");
|
||||
let def_id = tcx.map.local_def_id(node_id);
|
||||
let mut ecx = EvalContext::new(tcx, mir_map);
|
||||
let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit);
|
||||
let substs = tcx.mk_substs(subst::Substs::empty());
|
||||
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging");
|
||||
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs)
|
||||
.expect("should at least be able to allocate space for the main function's return value")
|
||||
.expect("main function should not be diverging");
|
||||
|
||||
ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr));
|
||||
ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr))
|
||||
.expect("could not allocate first stack frame");
|
||||
|
||||
if mir.arg_decls.len() == 2 {
|
||||
// start function
|
||||
let ptr_size = ecx.memory().pointer_size();
|
||||
let nargs = ecx.memory_mut().allocate(ptr_size);
|
||||
let nargs = ecx.memory_mut().allocate(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);
|
||||
let args = ecx.memory_mut().allocate(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;
|
||||
}
|
||||
|
||||
loop {
|
||||
for _ in 0..step_limit {
|
||||
match ecx.step() {
|
||||
Ok(true) => {}
|
||||
Ok(false) => break,
|
||||
Ok(false) => return,
|
||||
// FIXME: diverging functions can end up here in some future miri
|
||||
Err(e) => {
|
||||
report(tcx, &ecx, e);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
report(tcx, &ecx, EvalError::ExecutionTimeLimitReached);
|
||||
}
|
||||
|
||||
fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
|
||||
|
|
|
@ -29,15 +29,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let basic_block = &mir.basic_blocks()[block];
|
||||
|
||||
if let Some(ref stmt) = basic_block.statements.get(stmt) {
|
||||
let current_stack = self.stack.len();
|
||||
let mut new = Ok(0);
|
||||
ConstantExtractor {
|
||||
span: stmt.source_info.span,
|
||||
substs: self.substs(),
|
||||
def_id: self.frame().def_id,
|
||||
ecx: self,
|
||||
mir: &mir,
|
||||
new_constants: &mut new,
|
||||
}.visit_statement(block, stmt);
|
||||
if current_stack == self.stack.len() {
|
||||
if new? == 0 {
|
||||
self.statement(stmt)?;
|
||||
}
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
|
@ -46,15 +47,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let terminator = basic_block.terminator();
|
||||
let current_stack = self.stack.len();
|
||||
let mut new = Ok(0);
|
||||
ConstantExtractor {
|
||||
span: terminator.source_info.span,
|
||||
substs: self.substs(),
|
||||
def_id: self.frame().def_id,
|
||||
ecx: self,
|
||||
mir: &mir,
|
||||
new_constants: &mut new,
|
||||
}.visit_terminator(block, terminator);
|
||||
if current_stack == self.stack.len() {
|
||||
if new? == 0 {
|
||||
self.terminator(terminator)?;
|
||||
}
|
||||
// if ConstantExtractor added new frames, we don't execute anything here
|
||||
|
@ -92,6 +94,7 @@ struct ConstantExtractor<'a, 'b: 'a, 'tcx: 'b> {
|
|||
mir: &'a mir::Mir<'tcx>,
|
||||
def_id: DefId,
|
||||
substs: &'tcx subst::Substs<'tcx>,
|
||||
new_constants: &'a mut EvalResult<'tcx, u64>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
||||
|
@ -105,9 +108,22 @@ impl<'a, 'b, 'tcx> ConstantExtractor<'a, 'b, 'tcx> {
|
|||
return;
|
||||
}
|
||||
let mir = self.ecx.load_mir(def_id);
|
||||
let ptr = self.ecx.alloc_ret_ptr(mir.return_ty, substs).expect("there's no such thing as an unreachable static");
|
||||
self.ecx.statics.insert(cid.clone(), ptr);
|
||||
self.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr));
|
||||
self.try(|this| {
|
||||
let ptr = this.ecx.alloc_ret_ptr(mir.return_ty, substs)?;
|
||||
let ptr = ptr.expect("there's no such thing as an unreachable static");
|
||||
this.ecx.statics.insert(cid.clone(), ptr);
|
||||
this.ecx.push_stack_frame(def_id, span, mir, substs, Some(ptr))
|
||||
});
|
||||
}
|
||||
fn try<F: FnOnce(&mut Self) -> EvalResult<'tcx, ()>>(&mut self, f: F) {
|
||||
if let Ok(ref mut n) = *self.new_constants {
|
||||
*n += 1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if let Err(e) = f(self) {
|
||||
*self.new_constants = Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,10 +153,13 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ConstantExtractor<'a, 'b, 'tcx> {
|
|||
}
|
||||
let mir = self.mir.promoted[index].clone();
|
||||
let return_ty = mir.return_ty;
|
||||
let return_ptr = self.ecx.alloc_ret_ptr(return_ty, cid.substs).expect("there's no such thing as an unreachable static");
|
||||
let mir = CachedMir::Owned(Rc::new(mir));
|
||||
self.ecx.statics.insert(cid.clone(), return_ptr);
|
||||
self.ecx.push_stack_frame(self.def_id, constant.span, mir, self.substs, Some(return_ptr));
|
||||
self.try(|this| {
|
||||
let return_ptr = this.ecx.alloc_ret_ptr(return_ty, cid.substs)?;
|
||||
let return_ptr = return_ptr.expect("there's no such thing as an unreachable static");
|
||||
let mir = CachedMir::Owned(Rc::new(mir));
|
||||
this.ecx.statics.insert(cid.clone(), return_ptr);
|
||||
this.ecx.push_stack_frame(this.def_id, constant.span, mir, this.substs, Some(return_ptr))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,7 +212,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
let mir = self.load_mir(resolved_def_id);
|
||||
self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr);
|
||||
self.push_stack_frame(def_id, span, mir, resolved_substs, return_ptr)?;
|
||||
|
||||
for (i, (src, src_ty)) in arg_srcs.into_iter().enumerate() {
|
||||
let dest = self.frame().locals[i];
|
||||
|
@ -416,7 +416,7 @@ 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 ptr = self.memory.allocate(size as usize)?;
|
||||
self.memory.write_ptr(dest, ptr)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,10 @@ pub struct FunctionDefinition<'tcx> {
|
|||
pub struct Memory<'a, 'tcx> {
|
||||
/// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations)
|
||||
alloc_map: HashMap<AllocId, Allocation>,
|
||||
/// Number of virtual bytes allocated
|
||||
memory_usage: usize,
|
||||
/// Maximum number of virtual bytes that may be allocated
|
||||
memory_size: usize,
|
||||
/// Function "allocations". They exist solely so pointers have something to point to, and
|
||||
/// we can figure out what they point to.
|
||||
functions: HashMap<AllocId, FunctionDefinition<'tcx>>,
|
||||
|
@ -78,13 +82,15 @@ pub struct Memory<'a, 'tcx> {
|
|||
const ZST_ALLOC_ID: AllocId = AllocId(0);
|
||||
|
||||
impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
pub fn new(layout: &'a TargetDataLayout) -> Self {
|
||||
pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self {
|
||||
let mut mem = Memory {
|
||||
alloc_map: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
function_alloc_cache: HashMap::new(),
|
||||
next_id: AllocId(1),
|
||||
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
|
||||
|
@ -95,7 +101,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
};
|
||||
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
|
||||
// check that additional zst allocs work
|
||||
debug_assert!(mem.allocate(0).points_to_zst());
|
||||
debug_assert!(mem.allocate(0).unwrap().points_to_zst());
|
||||
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
|
||||
mem
|
||||
}
|
||||
|
@ -127,10 +133,18 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, size: usize) -> Pointer {
|
||||
pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> {
|
||||
if size == 0 {
|
||||
return Pointer::zst_ptr();
|
||||
return Ok(Pointer::zst_ptr());
|
||||
}
|
||||
if self.memory_size - self.memory_usage < size {
|
||||
return Err(EvalError::OutOfMemory {
|
||||
allocation_size: size,
|
||||
memory_size: self.memory_size,
|
||||
memory_usage: self.memory_usage,
|
||||
});
|
||||
}
|
||||
self.memory_usage += size;
|
||||
let alloc = Allocation {
|
||||
bytes: vec![0; size],
|
||||
relocations: BTreeMap::new(),
|
||||
|
@ -139,10 +153,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
let id = self.next_id;
|
||||
self.next_id.0 += 1;
|
||||
self.alloc_map.insert(id, alloc);
|
||||
Pointer {
|
||||
Ok(Pointer {
|
||||
alloc_id: id,
|
||||
offset: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
|
||||
|
@ -153,20 +167,23 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||
}
|
||||
if ptr.points_to_zst() {
|
||||
return Ok(self.allocate(new_size));
|
||||
return self.allocate(new_size);
|
||||
}
|
||||
|
||||
let size = self.get_mut(ptr.alloc_id)?.bytes.len();
|
||||
|
||||
if new_size > size {
|
||||
let amount = new_size - size;
|
||||
self.memory_usage += amount;
|
||||
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 {
|
||||
self.memory_usage -= size - new_size;
|
||||
self.clear_relocations(ptr.offset(new_size as isize), size - new_size)?;
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
alloc.bytes.truncate(new_size);
|
||||
alloc.bytes.shrink_to_fit();
|
||||
alloc.undef_mask.truncate(new_size);
|
||||
}
|
||||
|
||||
|
@ -183,7 +200,9 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
|
||||
}
|
||||
|
||||
if self.alloc_map.remove(&ptr.alloc_id).is_none() {
|
||||
if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) {
|
||||
self.memory_usage -= alloc.bytes.len();
|
||||
} else {
|
||||
debug!("deallocated a pointer twice: {}", ptr.alloc_id);
|
||||
// TODO(solson): Report error about erroneous free. This is blocked on properly tracking
|
||||
// already-dropped state since this if-statement is entered even in safe code without
|
||||
|
@ -710,6 +729,7 @@ impl UndefMask {
|
|||
fn truncate(&mut self, length: usize) {
|
||||
self.len = length;
|
||||
self.blocks.truncate(self.len / BLOCK_SIZE + 1);
|
||||
self.blocks.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
tests/compile-fail/oom.rs
Normal file
11
tests/compile-fail/oom.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![feature(custom_attribute)]
|
||||
#![miri(memory_size="0")]
|
||||
|
||||
fn bar() {
|
||||
let x = 5;
|
||||
assert_eq!(x, 6);
|
||||
}
|
||||
|
||||
fn main() { //~ ERROR tried to allocate 4 more bytes, but only 0 bytes are free of the 0 byte memory
|
||||
bar();
|
||||
}
|
39
tests/compile-fail/oom2.rs
Normal file
39
tests/compile-fail/oom2.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#![feature(custom_attribute)]
|
||||
#![miri(memory_size="1000")]
|
||||
|
||||
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
|
||||
//~^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
|
||||
//~|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
|
||||
//~|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
|
||||
//~|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
|
||||
//~|NOTE inside call to bar
|
||||
//~|NOTE inside call to bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { //~NOTE inside call to main
|
||||
bar(1);
|
||||
//~^NOTE inside call to bar
|
||||
}
|
20
tests/compile-fail/stack_limit.rs
Normal file
20
tests/compile-fail/stack_limit.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
#![feature(custom_attribute)]
|
||||
#![miri(stack_limit="2")]
|
||||
|
||||
fn bar() {
|
||||
foo();
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
cake(); //~ ERROR reached the configured maximum number of stack frames
|
||||
}
|
||||
|
||||
fn cake() {
|
||||
flubber();
|
||||
}
|
||||
|
||||
fn flubber() {}
|
||||
|
||||
fn main() {
|
||||
bar();
|
||||
}
|
9
tests/compile-fail/timeout.rs
Normal file
9
tests/compile-fail/timeout.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
//error-pattern: reached the configured maximum execution time
|
||||
#![feature(custom_attribute)]
|
||||
#![miri(step_limit="1000")]
|
||||
|
||||
fn main() {
|
||||
for i in 0..1000000 {
|
||||
assert!(i < 1000);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue