Summary
The PEGTL grammar (src/numsim_cas/parser/grammar.h, recursive rules expression/primary/unary/power) parses with unbounded C++ stack recursion and parse() (parser.cpp:60) sets no depth control. Deeply nested input segfaults instead of raising parse_error (reproduced, exit 139):
"("*20000 + "1" + ")"*20000 → SIGSEGV
- 20 000 leading unary minuses → SIGSEGV
- 20 000 nested
sin( → SIGSEGV
- the error path
"("*20000 + "1" with no closers → SIGSEGV (crashes instead of reporting the missing parens)
Threshold ~10–20k on a default 8 MB stack; lower on threads with small stacks. The parser is exactly the component fed untrusted text, and every other malformed input yields a catchable parse_error.
Severity: high (process crash on hostile/malformed input). Not among #231's deferred features.
Fix
Add a depth guard: a custom PEGTL control (or start/end actions on grammar::expression/primary) incrementing/decrementing a counter in parser_state, throwing syntax_error("expression nesting too deep", offset) beyond a cap (e.g. 512). Cheap stopgap: pre-scan in parse() counting max (/[/{/unary-minus nesting.
Tests (lock-in)
TEST(Parser, DeepNestingRaisesParseError) {
std::string deep(20000, '(');
deep += "1";
deep += std::string(20000, ')');
parser::symbol_table syms;
EXPECT_THROW(parser::parse(deep, syms), parser::parse_error);
std::string unclosed(20000, '(');
unclosed += "1";
EXPECT_THROW(parser::parse(unclosed, syms), parser::parse_error);
// sanity: moderate nesting still parses
std::string ok(200, '(');
ok += "1";
ok += std::string(200, ')');
EXPECT_NO_THROW(parser::parse(ok, syms));
}
Summary
The PEGTL grammar (
src/numsim_cas/parser/grammar.h, recursive rulesexpression/primary/unary/power) parses with unbounded C++ stack recursion andparse()(parser.cpp:60) sets no depth control. Deeply nested input segfaults instead of raisingparse_error(reproduced, exit 139):"("*20000 + "1" + ")"*20000→ SIGSEGVsin(→ SIGSEGV"("*20000 + "1"with no closers → SIGSEGV (crashes instead of reporting the missing parens)Threshold ~10–20k on a default 8 MB stack; lower on threads with small stacks. The parser is exactly the component fed untrusted text, and every other malformed input yields a catchable
parse_error.Severity: high (process crash on hostile/malformed input). Not among #231's deferred features.
Fix
Add a depth guard: a custom PEGTL control (or start/end actions on
grammar::expression/primary) incrementing/decrementing a counter inparser_state, throwingsyntax_error("expression nesting too deep", offset)beyond a cap (e.g. 512). Cheap stopgap: pre-scan inparse()counting max(/[/{/unary-minus nesting.Tests (lock-in)