Skip to content

Commit

Permalink
String pattern matching had exponential time complexity on pathologic…
Browse files Browse the repository at this point in the history
…al 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>
  • Loading branch information
oranagra and tom93 committed Feb 28, 2023
1 parent 18017df commit dcbfcb9
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 4 deletions.
27 changes: 23 additions & 4 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand All @@ -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 '?':
Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/keyspace.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
} {}
}

0 comments on commit dcbfcb9

Please sign in to comment.