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