Skip to content

Parser: unbounded recursion — 20k-deep nested input segfaults instead of raising parse_error (also on the unclosed-paren error path) #355

Description

@petlenz

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));
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions