Skip to content

Commit a5a2296

Browse files
committed
perf(vm): Only check for stack overflow when entering a function
1 parent e4e179d commit a5a2296

File tree

3 files changed

+86
-69
lines changed

3 files changed

+86
-69
lines changed

vm/src/channel.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ fn spawn_<'vm>(value: WithVM<'vm, Function<&'vm Thread, fn(())>>) -> VmResult<Ro
229229
};
230230
value_variant.clone().push(&mut context)?;
231231
context.push(ValueRepr::Int(0));
232-
context.context().stack.enter_scope(1, &*callable);
232+
context.context().stack.enter_scope(1, &*callable)?;
233233
}
234234
Ok(thread)
235235
}

vm/src/stack.rs

Lines changed: 51 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ use crate::{
1010
gc::{self, CloneUnrooted, CopyUnrooted, GcPtr, Trace},
1111
types::VmIndex,
1212
value::{ClosureData, DataStruct, ExternFunction, Value, ValueRepr},
13-
Variants,
13+
Error, Result, Variants,
1414
};
15+
use gc::Borrow;
1516

1617
pub trait StackPrimitive {
1718
fn push_to(&self, stack: &mut Stack);
@@ -199,6 +200,7 @@ pub trait StackState: CopyUnrooted + Sized {
199200
fn from_state(state: &State) -> &Self;
200201
fn from_state_mut(state: &mut State) -> &mut Self;
201202
fn to_state(&self) -> gc::Borrow<State>;
203+
fn max_stack_size(&self) -> VmIndex;
202204
}
203205

204206
impl StackState for State {
@@ -211,6 +213,14 @@ impl StackState for State {
211213
fn to_state(&self) -> gc::Borrow<State> {
212214
gc::Borrow::new(self)
213215
}
216+
217+
fn max_stack_size(&self) -> VmIndex {
218+
match self {
219+
State::Unknown => 0,
220+
State::Closure(closure) => closure.max_stack_size(),
221+
State::Extern(ext) => ext.max_stack_size(),
222+
}
223+
}
214224
}
215225

216226
impl StackState for ClosureState {
@@ -229,6 +239,9 @@ impl StackState for ClosureState {
229239
fn to_state(&self) -> gc::Borrow<State> {
230240
construct_gc!(State::Closure(@ gc::Borrow::new(self)))
231241
}
242+
fn max_stack_size(&self) -> VmIndex {
243+
self.closure.function.max_stack_size
244+
}
232245
}
233246

234247
impl StackState for ExternState {
@@ -247,6 +260,9 @@ impl StackState for ExternState {
247260
fn to_state(&self) -> gc::Borrow<State> {
248261
construct_gc!(State::Extern(@ gc::Borrow::new(self)))
249262
}
263+
fn max_stack_size(&self) -> VmIndex {
264+
0
265+
}
250266
}
251267

252268
#[derive(Debug, PartialEq)]
@@ -408,6 +424,7 @@ pub struct Stack {
408424
values: Vec<Value>,
409425
#[cfg_attr(feature = "serde_derive", serde(state))]
410426
frames: Vec<Frame<State>>,
427+
max_stack_size: VmIndex,
411428
}
412429

413430
unsafe impl Trace for Stack {
@@ -419,9 +436,14 @@ impl Stack {
419436
Stack {
420437
values: Vec::new(),
421438
frames: Vec::new(),
439+
max_stack_size: VmIndex::MAX,
422440
}
423441
}
424442

443+
pub fn set_max_stack_size(&mut self, max_stack_size: VmIndex) {
444+
self.max_stack_size = max_stack_size;
445+
}
446+
425447
fn assert_pop(&self, count: VmIndex) {
426448
let frame = self.frames.last().unwrap();
427449
let args = if let State::Extern(ExternState {
@@ -656,9 +678,9 @@ impl<'a: 'b, 'b> StackFrame<'b, State> {
656678
}
657679
}
658680

659-
pub fn new_frame(stack: &'b mut Stack, args: VmIndex, state: State) -> StackFrame<'b> {
660-
let frame = unsafe { Self::add_new_frame(stack, args, &state, false).unrooted() };
661-
StackFrame { stack, frame }
681+
pub fn new_frame(stack: &'b mut Stack, args: VmIndex, state: State) -> Result<StackFrame<'b>> {
682+
let frame = unsafe { Self::add_new_frame(stack, args, &state, false)?.unrooted() };
683+
Ok(StackFrame { stack, frame })
662684
}
663685
}
664686

@@ -825,7 +847,7 @@ where
825847
}
826848
}
827849

828-
pub(crate) fn enter_scope<T>(self, args: VmIndex, state: &T) -> StackFrame<'b, T>
850+
pub(crate) fn enter_scope<T>(self, args: VmIndex, state: &T) -> Result<StackFrame<'b, T>>
829851
where
830852
T: StackState,
831853
{
@@ -837,16 +859,16 @@ where
837859
args: VmIndex,
838860
state: &T,
839861
excess: bool,
840-
) -> StackFrame<'b, T>
862+
) -> Result<StackFrame<'b, T>>
841863
where
842864
T: StackState,
843865
{
844866
let stack = self.stack;
845-
let frame = unsafe { Self::add_new_frame(stack, args, state, excess).unrooted() };
846-
StackFrame { stack, frame }
867+
let frame = unsafe { Self::add_new_frame(stack, args, state, excess)?.unrooted() };
868+
Ok(StackFrame { stack, frame })
847869
}
848870

849-
pub(crate) fn exit_scope(self) -> Result<StackFrame<'b, State>, &'b mut Stack>
871+
pub(crate) fn exit_scope(self) -> std::result::Result<StackFrame<'b, State>, &'b mut Stack>
850872
where
851873
S: StackState,
852874
{
@@ -886,7 +908,7 @@ where
886908
args: VmIndex,
887909
state: &'gc T,
888910
excess: bool,
889-
) -> gc::Borrow<'gc, Frame<T>>
911+
) -> Result<Borrow<'gc, Frame<T>>>
890912
where
891913
T: StackState,
892914
{
@@ -911,13 +933,18 @@ where
911933
);
912934
}
913935
}
936+
// Before entering a function check that the stack cannot exceed `max_stack_size`
937+
if stack.len() + frame.state.max_stack_size() > stack.max_stack_size {
938+
return Err(Error::StackOverflow(stack.max_stack_size));
939+
}
940+
914941
// SAFETY The frame's gc pointers are scanned the `Stack::trace` since they are on
915942
// the stack
916943
unsafe {
917944
stack.frames.push(frame.to_state().clone_unrooted());
918945
}
919946
debug!("----> Store {} {:?}", stack.frames.len(), frame.to_state());
920-
frame
947+
Ok(frame)
921948
}
922949
}
923950

@@ -1158,15 +1185,15 @@ mod tests {
11581185
let _ = ::env_logger::try_init();
11591186

11601187
let mut stack = Stack::new();
1161-
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown);
1188+
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
11621189
frame.push(Int(0));
11631190
frame.push(Int(1));
11641191

1165-
frame = frame.enter_scope(2, &State::Unknown);
1192+
frame = frame.enter_scope(2, &State::Unknown).unwrap();
11661193
frame.push(Int(2));
11671194
frame.push(Int(3));
11681195

1169-
frame = frame.enter_scope(1, &State::Unknown);
1196+
frame = frame.enter_scope(1, &State::Unknown).unwrap();
11701197
frame.push(Int(4));
11711198
frame.push(Int(5));
11721199
frame.push(Int(6));
@@ -1193,14 +1220,14 @@ mod tests {
11931220

11941221
let mut stack = Stack::new();
11951222
{
1196-
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown);
1223+
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
11971224
frame.push(Int(0));
11981225
frame.push(Int(1));
1199-
let frame = frame.enter_scope(2, &*ExternState::new(&ext));
1226+
let frame = frame.enter_scope(2, &*ExternState::new(&ext)).unwrap();
12001227
let _lock = frame.into_lock();
12011228
}
12021229
// Panic as it attempts to access past the lock
1203-
StackFrame::new_frame(&mut stack, 1, State::Unknown);
1230+
StackFrame::new_frame(&mut stack, 1, State::Unknown).unwrap();
12041231

12051232
unsafe { gc.clear() }
12061233
}
@@ -1215,9 +1242,9 @@ mod tests {
12151242

12161243
let mut stack = Stack::new();
12171244
{
1218-
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown);
1245+
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
12191246
frame.push(Int(0));
1220-
let frame = frame.enter_scope(1, &*ExternState::new(&ext));
1247+
let frame = frame.enter_scope(1, &*ExternState::new(&ext)).unwrap();
12211248
let _lock = frame.into_lock();
12221249
}
12231250
// Panic as it attempts to pop a locked value
@@ -1235,14 +1262,14 @@ mod tests {
12351262

12361263
let mut stack = Stack::new();
12371264
let lock = {
1238-
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown);
1265+
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
12391266
frame.push(Int(0));
12401267
frame.push(Int(1));
1241-
let frame = frame.enter_scope(2, &*ExternState::new(&ext));
1268+
let frame = frame.enter_scope(2, &*ExternState::new(&ext)).unwrap();
12421269
frame.into_lock()
12431270
};
12441271
{
1245-
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown);
1272+
let mut frame = StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
12461273
frame.push(Int(2));
12471274
frame = frame.exit_scope().unwrap();
12481275
frame.exit_scope().unwrap_err();
@@ -1259,12 +1286,12 @@ mod tests {
12591286
let _ = ::env_logger::try_init();
12601287

12611288
let mut stack = Stack::new();
1262-
StackFrame::new_frame(&mut stack, 0, State::Unknown);
1289+
StackFrame::new_frame(&mut stack, 0, State::Unknown).unwrap();
12631290
let mut stack = StackFrame::<State>::current(&mut stack);
12641291
stack.push(Int(0));
12651292
stack.insert_slice(0, &[Int(2).into(), Int(1).into()]);
12661293
assert_eq!(&stack[..], [Int(2).into(), Int(1).into(), Int(0).into()]);
1267-
stack = stack.enter_scope(2, &State::Unknown);
1294+
stack = stack.enter_scope(2, &State::Unknown).unwrap();
12681295
stack.insert_slice(1, &[Int(10).into()]);
12691296
assert_eq!(&stack[..], [Int(1).into(), Int(10).into(), Int(0).into()]);
12701297
stack.insert_slice(1, &[]);

0 commit comments

Comments
 (0)