From 65fc1d6b01ade5e76fe712f93e2e108194c3291b Mon Sep 17 00:00:00 2001 From: Marius Kintel Date: Tue, 9 Oct 2012 23:32:56 -0400 Subject: Detect recursive execution of functions to avoid a stack overflow crash. Fixes #200 diff --git a/src/context.cc b/src/context.cc index f48cd86..a2a8d13 100644 --- a/src/context.cc +++ b/src/context.cc @@ -43,6 +43,7 @@ std::vector Context::ctx_stack; Context::Context(const Context *parent, const Module *library) : parent(parent), inst_p(NULL) { + if (parent) recursioncount = parent->recursioncount; ctx_stack.push_back(this); if (parent) document_path = parent->document_path; if (library) { @@ -130,10 +131,26 @@ Value Context::lookup_variable(const std::string &name, bool silent) const return Value(); } +class RecursionGuard +{ +public: + RecursionGuard(const Context &c, const std::string &name) : c(c), name(name) { c.recursioncount[name]++; } + ~RecursionGuard() { if (--c.recursioncount[name] == 0) c.recursioncount.erase(name); } + bool recursion_detected() const { return (c.recursioncount[name] > 100); } +private: + const Context &c; + const std::string &name; +}; + Value Context::evaluate_function(const std::string &name, const std::vector &argnames, const std::vector &argvalues) const { + RecursionGuard g(*this, name); + if (g.recursion_detected()) { + PRINTB("Recursion detected calling function '%s'", name); + return Value(); + } if (this->functions_p && this->functions_p->find(name) != this->functions_p->end()) return this->functions_p->find(name)->second->evaluate(this, argnames, argvalues); if (this->usedlibs_p) { @@ -144,8 +161,7 @@ Value Context::evaluate_function(const std::string &name, } } } - if (this->parent) - return this->parent->evaluate_function(name, argnames, argvalues); + if (this->parent) return this->parent->evaluate_function(name, argnames, argvalues); PRINTB("WARNING: Ignoring unknown function '%s'.", name); return Value(); } diff --git a/src/context.h b/src/context.h index f085e01..de5f1ca 100644 --- a/src/context.h +++ b/src/context.h @@ -41,6 +41,8 @@ public: static std::vector ctx_stack; + mutable unordered_map recursioncount; + private: typedef unordered_map ValueMap; ValueMap constants; diff --git a/testdata/scad/misc/recursion-tests.scad b/testdata/scad/misc/recursion-tests.scad new file mode 100644 index 0000000..2a07b91 --- /dev/null +++ b/testdata/scad/misc/recursion-tests.scad @@ -0,0 +1,2 @@ +function crash() = crash(); +echo(crash()); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 33f3547..68083ce 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -704,7 +704,8 @@ list(APPEND ECHO_FILES ${FUNCTION_FILES} ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-test.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/string-indexing.scad ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/vector-values.scad - ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad) + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/search-tests.scad + ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/recursion-tests.scad) list(APPEND DUMPTEST_FILES ${MINIMAL_FILES} ${FEATURES_FILES} ${EXAMPLE_FILES}) list(APPEND DUMPTEST_FILES ${CMAKE_SOURCE_DIR}/../testdata/scad/misc/escape-test.scad diff --git a/tests/regression/echotest/recursion-tests-expected.txt b/tests/regression/echotest/recursion-tests-expected.txt new file mode 100644 index 0000000..f4897ee --- /dev/null +++ b/tests/regression/echotest/recursion-tests-expected.txt @@ -0,0 +1,2 @@ +Recursion detected calling function 'crash' +ECHO: undef -- cgit v0.10.1