-
Notifications
You must be signed in to change notification settings - Fork 243
Closed
Labels
Milestone
Description
Consecutive escape sequences in ${name:+...} conditional substitution are mishandled after commit bf50eeef.
The removal of ptr -= 1 in find_text_end() causes the for loop's ptr++ to skip a character after check_escape() advances ptr, breaking consecutive escapes like \:\: inside conditional substitution text.
Example:
Pattern: foo(?<Bar>BAR)?
Replacement: X${Bar:+\:\:text}Y
Test 1: Subject 'foo' (Bar does NOT match)
Expected: 'XY'
Got: 'XtextY' (rc=1)
FAILED!
Test 2: Subject 'fooBAR' (Bar DOES match)
Expected: 'X::textY'
Got: 'X:extY' (rc=-57)
FAILED!
Program generating faulty output above:
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include <stdio.h>
#include <string.h>
int main(void) {
int errorcode;
PCRE2_SIZE erroffset;
PCRE2_UCHAR output[256];
PCRE2_SIZE outlength;
int rc;
int failed = 0;
const char *pattern = "foo(?<Bar>BAR)?";
const char *replacement = "X${Bar:+\\:\\:text}Y";
/* ^^^^
* Two consecutive escaped colons - the bug causes the second
* escape to be skipped when parsing the conditional text bounds.
*/
pcre2_code *re = pcre2_compile(
(PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, 0,
&errorcode, &erroffset, NULL);
if (re == NULL) {
fprintf(stderr, "Compile error %d at offset %zu\n", errorcode, erroffset);
return 1;
}
pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL);
printf("Pattern: %s\n", pattern);
printf("Replacement: %s\n\n", replacement);
/* Test 1: Group does NOT match - should output "XY" */
printf("Test 1: Subject 'foo' (Bar does NOT match)\n");
outlength = sizeof(output);
rc = pcre2_substitute(re, (PCRE2_SPTR)"foo", PCRE2_ZERO_TERMINATED, 0,
PCRE2_SUBSTITUTE_EXTENDED, match_data, NULL,
(PCRE2_SPTR)replacement, PCRE2_ZERO_TERMINATED, output, &outlength);
printf(" Expected: 'XY'\n");
printf(" Got: '%s' (rc=%d)\n", output, rc);
if (strcmp((char*)output, "XY") != 0) {
printf(" FAILED!\n");
failed = 1;
}
printf("\n");
/* Test 2: Group DOES match - should output "X::textY" */
printf("Test 2: Subject 'fooBAR' (Bar DOES match)\n");
outlength = sizeof(output);
rc = pcre2_substitute(re, (PCRE2_SPTR)"fooBAR", PCRE2_ZERO_TERMINATED, 0,
PCRE2_SUBSTITUTE_EXTENDED, match_data, NULL,
(PCRE2_SPTR)replacement, PCRE2_ZERO_TERMINATED, output, &outlength);
printf(" Expected: 'X::textY'\n");
printf(" Got: '%s' (rc=%d)\n", output, rc);
if (rc < 0 || strcmp((char*)output, "X::textY") != 0) {
printf(" FAILED!\n");
failed = 1;
}
printf("\n");
pcre2_match_data_free(match_data);
pcre2_code_free(re);
return failed;
}