Skip to content

Commit 0948e2d

Browse files
authored
Merge pull request rust-lang#43 from oli-obk/the_outer_limits
limit stack size, memory size and execution time
2 parents f4afb59 + 44bef25 commit 0948e2d

File tree

10 files changed

+240
-52
lines changed

10 files changed

+240
-52
lines changed

src/bin/miri.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use miri::{eval_main, run_mir_passes};
1313
use rustc::session::Session;
1414
use rustc::mir::mir_map::MirMap;
1515
use rustc_driver::{driver, CompilerCalls, Compilation};
16+
use syntax::ast::MetaItemKind;
1617

1718
struct MiriCompilerCalls;
1819

@@ -23,7 +24,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
2324
_: &getopts::Matches
2425
) -> driver::CompileController<'a> {
2526
let mut control = driver::CompileController::basic();
26-
27+
control.after_hir_lowering.callback = Box::new(|state| {
28+
state.session.plugin_attributes.borrow_mut().push(("miri".to_owned(), syntax::feature_gate::AttributeType::Whitelisted));
29+
});
2730
control.after_analysis.stop = Compilation::Stop;
2831
control.after_analysis.callback = Box::new(|state| {
2932
state.session.abort_if_errors();
@@ -33,9 +36,39 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
3336
let (node_id, _) = state.session.entry_fn.borrow()
3437
.expect("no main or start function found");
3538

39+
let krate = state.hir_crate.as_ref().unwrap();
40+
let mut memory_size = 100*1024*1024; // 100MB
41+
let mut step_limit = 1000_000;
42+
let mut stack_limit = 100;
43+
fn extract_str(lit: &syntax::ast::Lit) -> syntax::parse::token::InternedString {
44+
match lit.node {
45+
syntax::ast::LitKind::Str(ref s, _) => s.clone(),
46+
_ => panic!("attribute values need to be strings"),
47+
}
48+
}
49+
for attr in krate.attrs.iter() {
50+
match attr.node.value.node {
51+
MetaItemKind::List(ref name, _) if name != "miri" => {}
52+
MetaItemKind::List(_, ref items) => for item in items {
53+
match item.node {
54+
MetaItemKind::NameValue(ref name, ref value) => {
55+
match &**name {
56+
"memory_size" => memory_size = extract_str(value).parse().expect("not a number"),
57+
"step_limit" => step_limit = extract_str(value).parse().expect("not a number"),
58+
"stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"),
59+
_ => state.session.span_err(item.span, "unknown miri attribute"),
60+
}
61+
}
62+
_ => state.session.span_err(item.span, "miri attributes need to be of key = value kind"),
63+
}
64+
},
65+
_ => {},
66+
}
67+
}
68+
3669
let mut mir_map = MirMap { map: mir_map.map.clone() };
3770
run_mir_passes(tcx, &mut mir_map);
38-
eval_main(tcx, &mir_map, node_id);
71+
eval_main(tcx, &mir_map, node_id, memory_size, step_limit, stack_limit);
3972

4073
state.session.abort_if_errors();
4174
});

src/error.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ pub enum EvalError<'tcx> {
2929
ArrayIndexOutOfBounds(Span, u64, u64),
3030
Math(Span, ConstMathErr),
3131
InvalidChar(u32),
32+
OutOfMemory {
33+
allocation_size: usize,
34+
memory_size: usize,
35+
memory_usage: usize,
36+
},
37+
ExecutionTimeLimitReached,
38+
StackFrameLimitReached,
3239
}
3340

3441
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
@@ -69,6 +76,12 @@ impl<'tcx> Error for EvalError<'tcx> {
6976
"mathematical operation failed",
7077
EvalError::InvalidChar(..) =>
7178
"tried to interpret an invalid 32-bit value as a char",
79+
EvalError::OutOfMemory{..} =>
80+
"could not allocate more memory",
81+
EvalError::ExecutionTimeLimitReached =>
82+
"reached the configured maximum execution time",
83+
EvalError::StackFrameLimitReached =>
84+
"reached the configured maximum number of stack frames",
7285
}
7386
}
7487

@@ -90,6 +103,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
90103
write!(f, "{:?} at {:?}", err, span),
91104
EvalError::InvalidChar(c) =>
92105
write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c),
106+
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
107+
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
108+
allocation_size, memory_size - memory_usage, memory_size),
93109
_ => write!(f, "{}", self.description()),
94110
}
95111
}

src/interpreter/mod.rs

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ pub struct EvalContext<'a, 'tcx: 'a> {
4242

4343
/// The virtual call stack.
4444
stack: Vec<Frame<'a, 'tcx>>,
45+
46+
/// The maximum number of stack frames allowed
47+
stack_limit: usize,
4548
}
4649

4750
/// A stack frame.
@@ -133,24 +136,25 @@ enum ConstantKind {
133136
}
134137

135138
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
136-
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>) -> Self {
139+
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self {
137140
EvalContext {
138141
tcx: tcx,
139142
mir_map: mir_map,
140143
mir_cache: RefCell::new(DefIdMap()),
141-
memory: Memory::new(&tcx.data_layout),
144+
memory: Memory::new(&tcx.data_layout, memory_size),
142145
statics: HashMap::new(),
143146
stack: Vec::new(),
147+
stack_limit: stack_limit,
144148
}
145149
}
146150

147-
pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> Option<Pointer> {
151+
pub fn alloc_ret_ptr(&mut self, output_ty: ty::FnOutput<'tcx>, substs: &'tcx Substs<'tcx>) -> EvalResult<'tcx, Option<Pointer>> {
148152
match output_ty {
149153
ty::FnConverging(ty) => {
150154
let size = self.type_size_with_substs(ty, substs);
151-
Some(self.memory.allocate(size))
155+
self.memory.allocate(size).map(Some)
152156
}
153-
ty::FnDiverging => None,
157+
ty::FnDiverging => Ok(None),
154158
}
155159
}
156160

@@ -172,19 +176,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
172176
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
173177
macro_rules! i2p {
174178
($i:ident, $n:expr) => {{
175-
let ptr = self.memory.allocate($n);
179+
let ptr = self.memory.allocate($n)?;
176180
self.memory.write_int(ptr, $i as i64, $n)?;
177181
Ok(ptr)
178182
}}
179183
}
180184
match *const_val {
181185
Float(ConstFloat::F32(f)) => {
182-
let ptr = self.memory.allocate(4);
186+
let ptr = self.memory.allocate(4)?;
183187
self.memory.write_f32(ptr, f)?;
184188
Ok(ptr)
185189
},
186190
Float(ConstFloat::F64(f)) => {
187-
let ptr = self.memory.allocate(8);
191+
let ptr = self.memory.allocate(8)?;
188192
self.memory.write_f64(ptr, f)?;
189193
Ok(ptr)
190194
},
@@ -207,28 +211,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
207211
Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8),
208212
Str(ref s) => {
209213
let psize = self.memory.pointer_size();
210-
let static_ptr = self.memory.allocate(s.len());
211-
let ptr = self.memory.allocate(psize * 2);
214+
let static_ptr = self.memory.allocate(s.len())?;
215+
let ptr = self.memory.allocate(psize * 2)?;
212216
self.memory.write_bytes(static_ptr, s.as_bytes())?;
213217
self.memory.write_ptr(ptr, static_ptr)?;
214218
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
215219
Ok(ptr)
216220
}
217221
ByteStr(ref bs) => {
218222
let psize = self.memory.pointer_size();
219-
let static_ptr = self.memory.allocate(bs.len());
220-
let ptr = self.memory.allocate(psize);
223+
let static_ptr = self.memory.allocate(bs.len())?;
224+
let ptr = self.memory.allocate(psize)?;
221225
self.memory.write_bytes(static_ptr, bs)?;
222226
self.memory.write_ptr(ptr, static_ptr)?;
223227
Ok(ptr)
224228
}
225229
Bool(b) => {
226-
let ptr = self.memory.allocate(1);
230+
let ptr = self.memory.allocate(1)?;
227231
self.memory.write_bool(ptr, b)?;
228232
Ok(ptr)
229233
}
230234
Char(c) => {
231-
let ptr = self.memory.allocate(4);
235+
let ptr = self.memory.allocate(4)?;
232236
self.memory.write_uint(ptr, c as u64, 4)?;
233237
Ok(ptr)
234238
},
@@ -292,9 +296,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
292296
})
293297
}
294298

295-
pub fn push_stack_frame(&mut self, def_id: DefId, span: codemap::Span, mir: CachedMir<'a, 'tcx>, substs: &'tcx Substs<'tcx>,
296-
return_ptr: Option<Pointer>)
297-
{
299+
pub fn push_stack_frame(
300+
&mut self,
301+
def_id: DefId,
302+
span: codemap::Span,
303+
mir: CachedMir<'a, 'tcx>,
304+
substs: &'tcx Substs<'tcx>,
305+
return_ptr: Option<Pointer>,
306+
) -> EvalResult<'tcx, ()> {
298307
let arg_tys = mir.arg_decls.iter().map(|a| a.ty);
299308
let var_tys = mir.var_decls.iter().map(|v| v.ty);
300309
let temp_tys = mir.temp_decls.iter().map(|t| t.ty);
@@ -304,7 +313,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
304313

305314
::log_settings::settings().indentation += 1;
306315

307-
let locals: Vec<Pointer> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
316+
let locals: EvalResult<'tcx, Vec<Pointer>> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
308317
let size = self.type_size_with_substs(ty, substs);
309318
self.memory.allocate(size)
310319
}).collect();
@@ -313,14 +322,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
313322
mir: mir.clone(),
314323
block: mir::START_BLOCK,
315324
return_ptr: return_ptr,
316-
locals: locals,
325+
locals: locals?,
317326
var_offset: num_args,
318327
temp_offset: num_args + num_vars,
319328
span: span,
320329
def_id: def_id,
321330
substs: substs,
322331
stmt: 0,
323332
});
333+
if self.stack.len() > self.stack_limit {
334+
Err(EvalError::StackFrameLimitReached)
335+
} else {
336+
Ok(())
337+
}
324338
}
325339

326340
fn pop_stack_frame(&mut self) {
@@ -548,7 +562,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
548562

549563
Box(ty) => {
550564
let size = self.type_size(ty);
551-
let ptr = self.memory.allocate(size);
565+
let ptr = self.memory.allocate(size)?;
552566
self.memory.write_ptr(dest, ptr)?;
553567
}
554568

@@ -696,7 +710,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
696710
Item { def_id, substs } => {
697711
if let ty::TyFnDef(..) = ty.sty {
698712
// function items are zero sized
699-
Ok(self.memory.allocate(0))
713+
Ok(self.memory.allocate(0)?)
700714
} else {
701715
let cid = ConstantId {
702716
def_id: def_id,
@@ -935,37 +949,44 @@ pub fn eval_main<'a, 'tcx: 'a>(
935949
tcx: TyCtxt<'a, 'tcx, 'tcx>,
936950
mir_map: &'a MirMap<'tcx>,
937951
node_id: ast::NodeId,
952+
memory_size: usize,
953+
step_limit: u64,
954+
stack_limit: usize,
938955
) {
939956
let mir = mir_map.map.get(&node_id).expect("no mir for main function");
940957
let def_id = tcx.map.local_def_id(node_id);
941-
let mut ecx = EvalContext::new(tcx, mir_map);
958+
let mut ecx = EvalContext::new(tcx, mir_map, memory_size, stack_limit);
942959
let substs = tcx.mk_substs(subst::Substs::empty());
943-
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs).expect("main function should not be diverging");
960+
let return_ptr = ecx.alloc_ret_ptr(mir.return_ty, substs)
961+
.expect("should at least be able to allocate space for the main function's return value")
962+
.expect("main function should not be diverging");
944963

945-
ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr));
964+
ecx.push_stack_frame(def_id, mir.span, CachedMir::Ref(mir), substs, Some(return_ptr))
965+
.expect("could not allocate first stack frame");
946966

947967
if mir.arg_decls.len() == 2 {
948968
// start function
949969
let ptr_size = ecx.memory().pointer_size();
950-
let nargs = ecx.memory_mut().allocate(ptr_size);
970+
let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs");
951971
ecx.memory_mut().write_usize(nargs, 0).unwrap();
952-
let args = ecx.memory_mut().allocate(ptr_size);
972+
let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer");
953973
ecx.memory_mut().write_usize(args, 0).unwrap();
954974
ecx.frame_mut().locals[0] = nargs;
955975
ecx.frame_mut().locals[1] = args;
956976
}
957977

958-
loop {
978+
for _ in 0..step_limit {
959979
match ecx.step() {
960980
Ok(true) => {}
961-
Ok(false) => break,
981+
Ok(false) => return,
962982
// FIXME: diverging functions can end up here in some future miri
963983
Err(e) => {
964984
report(tcx, &ecx, e);
965-
break;
985+
return;
966986
}
967987
}
968988
}
989+
report(tcx, &ecx, EvalError::ExecutionTimeLimitReached);
969990
}
970991

971992
fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {

0 commit comments

Comments
 (0)