String pattern matching had exponential time complexity on pathological patterns (CVE-2022-36021) (#11858)
Authenticated users can use string matching commands with a specially crafted pattern to trigger a denial-of-service attack on Redis, causing it to hang and consume 100% CPU time. Co-authored-by: Tom Levy <tomlevy93@gmail.com>
This commit is contained in:
parent
18017df7c1
commit
dcbfcb916c
27
src/util.c
27
src/util.c
@ -53,8 +53,8 @@
|
||||
#define UNUSED(x) ((void)(x))
|
||||
|
||||
/* Glob-style pattern matching. */
|
||||
int stringmatchlen(const char *pattern, int patternLen,
|
||||
const char *string, int stringLen, int nocase)
|
||||
static int stringmatchlen_impl(const char *pattern, int patternLen,
|
||||
const char *string, int stringLen, int nocase, int *skipLongerMatches)
|
||||
{
|
||||
while(patternLen && stringLen) {
|
||||
switch(pattern[0]) {
|
||||
@ -66,12 +66,25 @@ int stringmatchlen(const char *pattern, int patternLen,
|
||||
if (patternLen == 1)
|
||||
return 1; /* match */
|
||||
while(stringLen) {
|
||||
if (stringmatchlen(pattern+1, patternLen-1,
|
||||
string, stringLen, nocase))
|
||||
if (stringmatchlen_impl(pattern+1, patternLen-1,
|
||||
string, stringLen, nocase, skipLongerMatches))
|
||||
return 1; /* match */
|
||||
if (*skipLongerMatches)
|
||||
return 0; /* no match */
|
||||
string++;
|
||||
stringLen--;
|
||||
}
|
||||
/* There was no match for the rest of the pattern starting
|
||||
* from anywhere in the rest of the string. If there were
|
||||
* any '*' earlier in the pattern, we can terminate the
|
||||
* search early without trying to match them to longer
|
||||
* substrings. This is because a longer match for the
|
||||
* earlier part of the pattern would require the rest of the
|
||||
* pattern to match starting later in the string, and we
|
||||
* have just determined that there is no match for the rest
|
||||
* of the pattern starting from anywhere in the current
|
||||
* string. */
|
||||
*skipLongerMatches = 1;
|
||||
return 0; /* no match */
|
||||
break;
|
||||
case '?':
|
||||
@ -173,6 +186,12 @@ int stringmatchlen(const char *pattern, int patternLen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stringmatchlen(const char *pattern, int patternLen,
|
||||
const char *string, int stringLen, int nocase) {
|
||||
int skipLongerMatches = 0;
|
||||
return stringmatchlen_impl(pattern,patternLen,string,stringLen,nocase,&skipLongerMatches);
|
||||
}
|
||||
|
||||
int stringmatch(const char *pattern, const char *string, int nocase) {
|
||||
return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
|
||||
}
|
||||
|
@ -493,4 +493,10 @@ foreach {type large} [array get largevalue] {
|
||||
r keys *
|
||||
r keys *
|
||||
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
|
||||
|
||||
test {Regression for pattern matching long nested loops} {
|
||||
r flushdb
|
||||
r SET aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 1
|
||||
r KEYS "a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*b"
|
||||
} {}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user