Instead of collapsing duplicate repetition characters, reject them.

This is implementation-specific behavior--some regcomp(3) will
reject duplicate repetition characters (BSD), others will try to
support them (Glibc) but may allocate excessive amounts of memory.
This commit is contained in:
Todd C. Miller
2022-12-17 11:32:24 -07:00
parent 2f32b45d59
commit f0530b66d3

View File

@@ -36,30 +36,23 @@
static char errbuf[1024]; static char errbuf[1024];
/* /*
* Like strdup but collapses repeated '?', '*' and '+' ops in a regex. * Check pattern for invalid repetition sequences.
* Glibc regcomp() has a bug where it uses excessive memory for repeated * This is implementation-specific behavior, not all regcomp(3) forbid them.
* '+' ops. Collapse them to avoid running the fuzzer out of memory. * Glibc allows it but uses excessive memory for repeated '+' ops.
*/ */
static char * static int
dup_pattern(const char *src) check_pattern(const char *pattern)
{ {
char *dst, *ret; debug_decl(check_pattern, SUDO_DEBUG_UTIL);
const char *cp = pattern;
char ch, prev = '\0'; char ch, prev = '\0';
size_t len;
debug_decl(dup_pattern, SUDO_DEBUG_UTIL);
len = strlen(src); while ((ch = *cp++) != '\0') {
ret = malloc(len + 1);
if (ret == NULL)
debug_return_ptr(NULL);
dst = ret;
while ((ch = *src++) != '\0') {
switch (ch) { switch (ch) {
case '\\': case '\\':
if (*src != '\0') { if (*cp != '\0') {
*dst++ = '\\'; /* Skip escaped character. */
*dst++ = *src++; cp++;
prev = '\0'; prev = '\0';
continue; continue;
} }
@@ -67,17 +60,16 @@ dup_pattern(const char *src)
case '?': case '?':
case '*': case '*':
case '+': case '+':
if (ch == prev) { if (prev == '?' || prev == '*' || prev == '+') {
continue; /* Invalid repetition operator. */
debug_return_int(REG_BADRPT);
} }
break; break;
} }
*dst++ = ch;
prev = ch; prev = ch;
} }
*dst = '\0';
debug_return_ptr(ret); debug_return_int(0);
} }
/* /*
@@ -108,22 +100,19 @@ sudo_regex_compile_v1(void *v, const char *pattern, const char **errstr)
cp = pattern[0] == '^' ? pattern + 1 : pattern; cp = pattern[0] == '^' ? pattern + 1 : pattern;
if (strncmp(cp, "(?i)", 4) == 0) { if (strncmp(cp, "(?i)", 4) == 0) {
cflags |= REG_ICASE; cflags |= REG_ICASE;
copy = dup_pattern(pattern + 4); copy = strdup(pattern + 4);
if (copy == NULL) { if (copy == NULL) {
*errstr = N_("unable to allocate memory"); *errstr = N_("unable to allocate memory");
debug_return_bool(false); debug_return_bool(false);
} }
if (pattern[0] == '^') if (pattern[0] == '^')
copy[0] = '^'; copy[0] = '^';
} else { pattern = copy;
copy = dup_pattern(pattern);
if (copy == NULL) {
*errstr = N_("unable to allocate memory");
debug_return_bool(false);
}
} }
errcode = regcomp(preg, copy, cflags); errcode = check_pattern(pattern);
if (errcode == 0)
errcode = regcomp(preg, pattern, cflags);
if (errcode == 0) { if (errcode == 0) {
if (preg == &rebuf) if (preg == &rebuf)
regfree(&rebuf); regfree(&rebuf);