Skip to content

Commit

Permalink
cap recursion depth on macros to avoid stack overflows
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwparas committed Dec 7, 2023
1 parent 907e3a2 commit 4f77eff
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 1 deletion.
40 changes: 39 additions & 1 deletion crates/steel-core/src/parser/expand_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::compiler::passes::Folder;
use crate::compiler::program::REQUIRE_DYLIB;
use crate::parser::ast::ExprKind;
use crate::parser::parser::SyntaxObject;
use crate::parser::span_visitor::get_span;
use crate::steel_vm::builtin::BuiltInModule;
use crate::steel_vm::engine::ModuleContainer;
use crate::{compiler::program::REQUIRE_BUILTIN, rvals::Result};
Expand Down Expand Up @@ -219,6 +220,7 @@ pub fn expand_kernel_in_env(
changed: false,
builtin_modules,
environment: Some(Cow::from(env)),
depth: 0,
}
.visit(expr)
}
Expand All @@ -233,6 +235,7 @@ pub fn expand_kernel(
changed: false,
builtin_modules,
environment: None,
depth: 0,
}
.visit(expr)
}
Expand All @@ -242,6 +245,7 @@ pub struct KernelExpander<'a> {
pub(crate) changed: bool,
builtin_modules: ModuleContainer,
environment: Option<Cow<'static, str>>,
depth: usize,
}

impl<'a> KernelExpander<'a> {
Expand All @@ -251,6 +255,7 @@ impl<'a> KernelExpander<'a> {
changed: false,
builtin_modules,
environment: None,
depth: 0,
}
}

Expand Down Expand Up @@ -719,7 +724,14 @@ impl<'a> ConsumingVisitor for KernelExpander<'a> {
.unwrap_or("default"),
)?;
self.changed = true;
return self.visit(expanded);

self.depth += 1;

let result = self.visit(expanded);

self.depth -= 1;

return result;
}
}

Expand Down Expand Up @@ -918,6 +930,32 @@ impl<'a> ConsumingVisitor for KernelExpander<'a> {

Ok(ExprKind::Let(l))
}

fn visit(&mut self, expr: ExprKind) -> Self::Output {
if self.depth > 96 {
stop!(BadSyntax => "Current expansion depth of defmacro style macros exceeded: depth capped at 96"; get_span(&expr));
}

let res = match expr {
ExprKind::If(f) => self.visit_if(f),
ExprKind::Define(d) => self.visit_define(d),
ExprKind::LambdaFunction(l) => self.visit_lambda_function(l),
ExprKind::Begin(b) => self.visit_begin(b),
ExprKind::Return(r) => self.visit_return(r),
ExprKind::Let(l) => self.visit_let(l),
ExprKind::Quote(q) => self.visit_quote(q),
ExprKind::Macro(m) => self.visit_macro(m),
ExprKind::Atom(a) => self.visit_atom(a),
ExprKind::List(l) => self.visit_list(l),
ExprKind::SyntaxRules(s) => self.visit_syntax_rules(s),
ExprKind::Set(s) => self.visit_set(s),
ExprKind::Require(r) => self.visit_require(r),
};

// self.depth -= 1;

res
}
}

fn _define_quoted_ast_node(ast_name: ExprKind, expanded_expr: &ExprKind) -> ExprKind {
Expand Down
4 changes: 4 additions & 0 deletions crates/steel-core/src/tests/failure/capped_depth_defmacro.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
;; Should stop before we get a stack overflow
(defmacro (foo x) x)

(foo (define bar 10))
1 change: 1 addition & 0 deletions crates/steel-core/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ test_harness_success! {
}

test_harness_failure! {
capped_depth_defmacro,
function_used_before_definition,
identifier_used_before_definition,
local_struct_inaccessible,
Expand Down

0 comments on commit 4f77eff

Please sign in to comment.