forked from pool/redis
7616b2211a
Fix CVE-2022-36021 (bsc#1208790) & Fix CVE-2023-25155 (bsc#1208793) OBS-URL: https://build.opensuse.org/request/show/1068515 OBS-URL: https://build.opensuse.org/package/show/server:database/redis?expand=0&rev=218
89 lines
3.5 KiB
Diff
89 lines
3.5 KiB
Diff
From 0825552565e5fdab2e87950579c4f0bedded3e3c Mon Sep 17 00:00:00 2001
|
|
From: Tom Levy <tomlevy93@gmail.com>
|
|
Date: Tue, 21 Feb 2023 15:14:30 +0200
|
|
Subject: [PATCH] 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.
|
|
---
|
|
src/util.c | 27 +++++++++++++++++++++++----
|
|
tests/unit/keyspace.tcl | 6 ++++++
|
|
2 files changed, 29 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/util.c b/src/util.c
|
|
index e1524b5e3..8ce2c5fca 100644
|
|
--- a/src/util.c
|
|
+++ b/src/util.c
|
|
@@ -50,8 +50,8 @@
|
|
#include "config.h"
|
|
|
|
/* 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]) {
|
|
@@ -63,12 +63,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 '?':
|
|
@@ -170,6 +183,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);
|
|
}
|
|
diff --git a/tests/unit/keyspace.tcl b/tests/unit/keyspace.tcl
|
|
index f5f971140..437f71fa1 100644
|
|
--- a/tests/unit/keyspace.tcl
|
|
+++ b/tests/unit/keyspace.tcl
|
|
@@ -489,4 +489,10 @@ start_server {tags {"keyspace"}} {
|
|
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"
|
|
+ } {}
|
|
}
|
|
--
|
|
2.35.3
|
|
|