Skip to content

Commit 60d9f97

Browse files
committed
Address issue #1539 - infinite loop in peg split
Other looping rules ensure forward progress by terminating if an iteration is at the same location as the previous iteration. Do the same for split.
1 parent f252933 commit 60d9f97

File tree

2 files changed

+31
-17
lines changed

2 files changed

+31
-17
lines changed

src/core/peg.c

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Calvin Rose
2+
* Copyright (c) 2025 Calvin Rose
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to
@@ -549,36 +549,39 @@ static const uint8_t *peg_rule(
549549
const uint32_t *rule_separator = s->bytecode + rule[1];
550550
const uint32_t *rule_subpattern = s->bytecode + rule[2];
551551

552-
const uint8_t *separator_end = NULL;
553-
do {
554-
const uint8_t *text_start = text;
552+
const uint8_t *chunk_start = text;
553+
const uint8_t *chunk_end = NULL;
554+
555+
while (text <= saved_end) {
556+
/* Find next split (or end of text) */
555557
CapState cs = cap_save(s);
556558
down1(s);
557-
while (text <= s->text_end) {
558-
separator_end = peg_rule(s, rule_separator, text);
559+
while (text <= saved_end) {
560+
chunk_end = text;
561+
const uint8_t *check = peg_rule(s, rule_separator, text);
559562
cap_load(s, cs);
560-
if (separator_end) {
563+
if (check) {
564+
text = check;
561565
break;
562566
}
563567
text++;
564568
}
565569
up1(s);
566570

567-
if (separator_end) {
568-
s->text_end = text;
569-
text = separator_end;
570-
}
571-
571+
/* Match between splits */
572+
s->text_end = chunk_end;
572573
down1(s);
573-
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
574+
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, chunk_start);
574575
up1(s);
575576
s->text_end = saved_end;
577+
if (!subpattern_end) return NULL; /* Don't match anything */
576578

577-
if (!subpattern_end) {
578-
return NULL;
579-
}
580-
} while (separator_end);
579+
/* Ensure forward progress */
580+
if (text == chunk_start) return NULL;
581+
chunk_start = text;
582+
}
581583

584+
s->text_end = saved_end;
582585
return s->text_end;
583586
}
584587

test/suite-peg.janet

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,5 +772,16 @@
772772
"5:apple6:banana6:cherry"
773773
@["apple" "banana" "cherry"])
774774

775+
# Issue #1539 - make sure split with "" doesn't infinite loop/oom
776+
(test "issue 1539"
777+
~(split "" (capture (to -1)))
778+
"hello there friends"
779+
nil)
780+
781+
(test "issue 1539 pt. 2"
782+
~(split "," (capture 0))
783+
"abc123,,,,"
784+
@["" "" "" "" ""])
785+
775786
(end-suite)
776787

0 commit comments

Comments
 (0)