Skip to content

Commit 2f73cea

Browse files
committed
Review fixes on #355: cap caret chains, honor the full space set
Two probe-confirmed bypasses of the new depth guard: the ^ chain is right-recursive in the grammar (50k carets still overflowed the stack), and the minus-run reset treated \v/\f as run breakers while PEGTL's space rule accepts them ('-\v' x 50000 crashed). Every ^ now counts toward the cap regardless of position, and the run persists across the full PEGTL space set. Lock-ins cover both bypasses plus a 100-caret happy path. Signed-off-by: petlenz <peterlenz89.pl@gmail.com>
1 parent 812d00f commit 2f73cea

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

src/numsim_cas/parser/parser.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,15 @@ syntax_error translate_pegtl_error(pegtl::parse_error const &e,
4646
// runs (whitespace does not reset a run: "- - -x" recurses per minus).
4747
void check_nesting_depth(std::string_view source) {
4848
constexpr std::size_t max_depth = 512;
49+
const auto is_space = [](char c) {
50+
// must cover PEGTL's full space set or a whitespace variant resets
51+
// the run and bypasses the guard (review on #355)
52+
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' ||
53+
c == '\f';
54+
};
4955
std::size_t depth = 0;
5056
std::size_t minus_run = 0;
57+
std::size_t caret_count = 0;
5158
for (std::size_t i = 0; i < source.size(); ++i) {
5259
const char c = source[i];
5360
if (c == '(' || c == '[' || c == '{') {
@@ -58,12 +65,18 @@ void check_nesting_depth(std::string_view source) {
5865
if (depth > 0) {
5966
--depth;
6067
}
68+
} else if (c == '^') {
69+
// every ^ contributes one right-recursion level regardless of
70+
// position (review on #355: 50k carets overflowed the stack)
71+
if (++caret_count > max_depth) {
72+
throw syntax_error("expression nesting too deep", i, source);
73+
}
6174
}
6275
if (c == '-') {
6376
if (++minus_run > max_depth) {
6477
throw syntax_error("expression nesting too deep", i, source);
6578
}
66-
} else if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
79+
} else if (!is_space(c)) {
6780
minus_run = 0;
6881
}
6982
}

tests/ParserTest.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2170,11 +2170,29 @@ TEST(ParserDepthGuard, DeepNestingRaisesParseError) {
21702170
EXPECT_THROW((void)numsim::cas::parser::parse(minuses, syms),
21712171
numsim::cas::parser::parse_error);
21722172

2173+
// review on #355: caret chains and PEGTL-space-separated minus runs
2174+
// are recursion drivers too
2175+
std::string carets = "1";
2176+
for (int i = 0; i < 20000; ++i)
2177+
carets += "^1";
2178+
EXPECT_THROW((void)numsim::cas::parser::parse(carets, syms),
2179+
numsim::cas::parser::parse_error);
2180+
std::string vminus;
2181+
for (int i = 0; i < 20000; ++i)
2182+
vminus += "-\v";
2183+
vminus += "1";
2184+
EXPECT_THROW((void)numsim::cas::parser::parse(vminus, syms),
2185+
numsim::cas::parser::parse_error);
2186+
21732187
// moderate nesting still parses
21742188
std::string ok(200, '(');
21752189
ok += "1";
21762190
ok += std::string(200, ')');
21772191
EXPECT_NO_THROW((void)numsim::cas::parser::parse(ok, syms));
2192+
std::string ok2 = "1";
2193+
for (int i = 0; i < 100; ++i)
2194+
ok2 += "^1";
2195+
EXPECT_NO_THROW((void)numsim::cas::parser::parse(ok2, syms));
21782196
}
21792197

21802198
#endif // NUMSIM_CAS_PARSER_ENABLED

0 commit comments

Comments
 (0)