diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 6997bbd011d..e11ab5a8609 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -40,6 +40,7 @@ fn build_controller( 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(), @@ -55,6 +56,7 @@ fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { 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"), } } @@ -67,7 +69,7 @@ fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString { let mut mir_map = MirMap { map: mir_map.map.clone() }; run_mir_passes(tcx, &mut mir_map); - eval_main(tcx, &mir_map, node_id, memory_size, step_limit); + eval_main(tcx, &mir_map, node_id, memory_size, step_limit, stack_limit); state.session.abort_if_errors(); }); diff --git a/src/error.rs b/src/error.rs index 5c2410ed840..9952cfd9a7e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -35,6 +35,7 @@ pub enum EvalError<'tcx> { memory_usage: u64, }, ExecutionTimeLimitReached, + StackFrameLimitReached, } pub type EvalResult<'tcx, T> = Result>; @@ -79,6 +80,8 @@ fn description(&self) -> &str { "could not allocate more memory", EvalError::ExecutionTimeLimitReached => "reached the configured maximum execution time", + EvalError::StackFrameLimitReached => + "reached the configured maximum number of stack frames", } } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 5b8947ec735..34fa9d8de6f 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -42,6 +42,9 @@ pub struct EvalContext<'a, 'tcx: 'a> { /// The virtual call stack. stack: Vec>, + + /// The maximum number of stack frames allowed + stack_limit: usize, } /// A stack frame. @@ -133,7 +136,8 @@ enum ConstantKind { } impl<'a, 'tcx> EvalContext<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64) -> Self { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64, stack_limit: u64) -> Self { + assert_eq!(stack_limit as usize as u64, stack_limit); EvalContext { tcx: tcx, mir_map: mir_map, @@ -141,6 +145,7 @@ pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: memory: Memory::new(&tcx.data_layout, memory_size), statics: HashMap::new(), stack: Vec::new(), + stack_limit: stack_limit as usize, } } @@ -316,7 +321,11 @@ pub fn push_stack_frame( substs: substs, stmt: 0, }); - Ok(()) + if self.stack.len() > self.stack_limit { + Err(EvalError::StackFrameLimitReached) + } else { + Ok(()) + } } fn pop_stack_frame(&mut self) { @@ -930,10 +939,11 @@ pub fn eval_main<'a, 'tcx: 'a>( node_id: ast::NodeId, memory_size: u64, step_limit: u64, + stack_limit: u64, ) { 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, memory_size); + 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("should at least be able to allocate space for the main function's return value") diff --git a/tests/compile-fail/stack_limit.rs b/tests/compile-fail/stack_limit.rs new file mode 100644 index 00000000000..3b6d4186dc9 --- /dev/null +++ b/tests/compile-fail/stack_limit.rs @@ -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(); +}