mirror of https://mirror.osredm.com/root/redis.git
String pattern matching had exponential time complexity on pathological patterns (CVE-2022-36021)
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. (cherry picked from commit e75f92047c22e659d49bba3a083cd0c9935f21e6) (cherry picked from commit e8a9d3f63aebf6065d69bd0125d4b9c367f88def)
This commit is contained in:
parent
789f6a95db
commit
8b565570f2
27
src/util.c
27
src/util.c
|
@ -45,8 +45,8 @@
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
|
|
||||||
/* Glob-style pattern matching. */
|
/* Glob-style pattern matching. */
|
||||||
int stringmatchlen(const char *pattern, int patternLen,
|
static int stringmatchlen_impl(const char *pattern, int patternLen,
|
||||||
const char *string, int stringLen, int nocase)
|
const char *string, int stringLen, int nocase, int *skipLongerMatches)
|
||||||
{
|
{
|
||||||
while(patternLen && stringLen) {
|
while(patternLen && stringLen) {
|
||||||
switch(pattern[0]) {
|
switch(pattern[0]) {
|
||||||
|
@ -58,12 +58,25 @@ int stringmatchlen(const char *pattern, int patternLen,
|
||||||
if (patternLen == 1)
|
if (patternLen == 1)
|
||||||
return 1; /* match */
|
return 1; /* match */
|
||||||
while(stringLen) {
|
while(stringLen) {
|
||||||
if (stringmatchlen(pattern+1, patternLen-1,
|
if (stringmatchlen_impl(pattern+1, patternLen-1,
|
||||||
string, stringLen, nocase))
|
string, stringLen, nocase, skipLongerMatches))
|
||||||
return 1; /* match */
|
return 1; /* match */
|
||||||
|
if (*skipLongerMatches)
|
||||||
|
return 0; /* no match */
|
||||||
string++;
|
string++;
|
||||||
stringLen--;
|
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 */
|
return 0; /* no match */
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
|
@ -165,6 +178,12 @@ int stringmatchlen(const char *pattern, int patternLen,
|
||||||
return 0;
|
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) {
|
int stringmatch(const char *pattern, const char *string, int nocase) {
|
||||||
return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
|
return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,4 +272,10 @@ start_server {tags {"keyspace"}} {
|
||||||
r keys *
|
r keys *
|
||||||
r keys *
|
r keys *
|
||||||
} {dlskeriewrioeuwqoirueioqwrueoqwrueqw}
|
} {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…
Reference in New Issue