Skip to content

Commit 0672e81

Browse files
committed
fix: make ypath expression parser fully iterative
The expression parser had a recursive call in ypath_parse_primary when handling parentheses, which called back into ypath_parse_expr. This caused stack overflow crashes when parsing deeply nested parentheses (e.g., from fuzzer inputs with thousands of '(' characters). The fix moves parenthesis handling into ypath_parse_expr's operator stack by adding a paren marker. This makes parsing fully iterative and bounded by YPATH_EXPR_STACK_CAP (64), which now limits nesting depth gracefully with an "expression too complex" error.
1 parent 9e977de commit 0672e81

1 file changed

Lines changed: 32 additions & 13 deletions

File tree

src/cyaml_path.c

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -726,14 +726,6 @@ static ypath_expr_t* ypath_parse_primary(ypath_parser_t* p)
726726
e->v.path.count = p->pool->step_count - start_count;
727727
return e;
728728
}
729-
case YPATH_TOK_LPAREN:
730-
ypath_lex_next(&p->lex);
731-
e = ypath_parse_expr(p);
732-
if (!e)
733-
return NULL;
734-
if (!ypath_expect(p, YPATH_TOK_RPAREN))
735-
return NULL;
736-
return e;
737729
default:
738730
ypath_parse_err(p, "expected expression");
739731
return NULL;
@@ -802,6 +794,7 @@ typedef struct {
802794
ypath_op_t op;
803795
int prec;
804796
bool unary;
797+
bool paren;
805798
} ypath_op_entry_t;
806799

807800
typedef struct {
@@ -846,16 +839,22 @@ static ypath_expr_t* ypath_parse_expr(ypath_parser_t* p)
846839
{
847840
ypath_expr_stack_t s = { .operand_count = 0, .op_count = 0 };
848841
bool expect_operand = true;
842+
int paren_depth = 0;
849843

850844
for (;;) {
851845
if (expect_operand) {
852-
while (p->lex.tok.type == YPATH_TOK_MINUS || p->lex.tok.type == YPATH_TOK_BANG) {
846+
while (p->lex.tok.type == YPATH_TOK_MINUS || p->lex.tok.type == YPATH_TOK_BANG || p->lex.tok.type == YPATH_TOK_LPAREN) {
853847
if (s.op_count >= YPATH_EXPR_STACK_CAP) {
854848
ypath_parse_err(p, "expression too complex");
855849
return NULL;
856850
}
857-
ypath_op_t op = (p->lex.tok.type == YPATH_TOK_MINUS) ? YPATH_OP_NEG : YPATH_OP_NOT;
858-
s.ops[s.op_count++] = (ypath_op_entry_t) { op, YPATH_UNARY_PREC, true };
851+
if (p->lex.tok.type == YPATH_TOK_LPAREN) {
852+
s.ops[s.op_count++] = (ypath_op_entry_t) { 0, -1, false, true };
853+
paren_depth++;
854+
} else {
855+
ypath_op_t op = (p->lex.tok.type == YPATH_TOK_MINUS) ? YPATH_OP_NEG : YPATH_OP_NOT;
856+
s.ops[s.op_count++] = (ypath_op_entry_t) { op, YPATH_UNARY_PREC, true, false };
857+
}
859858
ypath_lex_next(&p->lex);
860859
}
861860

@@ -873,24 +872,44 @@ static ypath_expr_t* ypath_parse_expr(ypath_parser_t* p)
873872
return NULL;
874873
expect_operand = false;
875874
} else {
875+
if (p->lex.tok.type == YPATH_TOK_RPAREN && paren_depth > 0) {
876+
while (s.op_count > 0 && !s.ops[s.op_count - 1].paren)
877+
if (!ypath_expr_stack_reduce(p, &s))
878+
return NULL;
879+
if (s.op_count > 0 && s.ops[s.op_count - 1].paren) {
880+
s.op_count--;
881+
paren_depth--;
882+
}
883+
while (s.op_count > 0 && s.ops[s.op_count - 1].unary)
884+
if (!ypath_expr_stack_reduce(p, &s))
885+
return NULL;
886+
ypath_lex_next(&p->lex);
887+
continue;
888+
}
889+
876890
int prec = ypath_op_prec(p->lex.tok.type);
877891
if (prec == 0)
878892
break;
879893

880-
while (s.op_count > 0 && !s.ops[s.op_count - 1].unary && s.ops[s.op_count - 1].prec >= prec)
894+
while (s.op_count > 0 && !s.ops[s.op_count - 1].paren && !s.ops[s.op_count - 1].unary && s.ops[s.op_count - 1].prec >= prec)
881895
if (!ypath_expr_stack_reduce(p, &s))
882896
return NULL;
883897

884898
if (s.op_count >= YPATH_EXPR_STACK_CAP) {
885899
ypath_parse_err(p, "expression too complex");
886900
return NULL;
887901
}
888-
s.ops[s.op_count++] = (ypath_op_entry_t) { ypath_tok_to_op(p->lex.tok.type), prec, false };
902+
s.ops[s.op_count++] = (ypath_op_entry_t) { ypath_tok_to_op(p->lex.tok.type), prec, false, false };
889903
ypath_lex_next(&p->lex);
890904
expect_operand = true;
891905
}
892906
}
893907

908+
if (paren_depth > 0) {
909+
ypath_parse_err(p, "unclosed parenthesis");
910+
return NULL;
911+
}
912+
894913
while (s.op_count > 0)
895914
if (!ypath_expr_stack_reduce(p, &s))
896915
return NULL;

0 commit comments

Comments
 (0)