summaryrefslogtreecommitdiff
path: root/CVE-2022-36021.patch
diff options
context:
space:
mode:
Diffstat (limited to 'CVE-2022-36021.patch')
-rw-r--r--CVE-2022-36021.patch92
1 files changed, 92 insertions, 0 deletions
diff --git a/CVE-2022-36021.patch b/CVE-2022-36021.patch
new file mode 100644
index 0000000..0eb5b30
--- /dev/null
+++ b/CVE-2022-36021.patch
@@ -0,0 +1,92 @@
+From dcbfcb916ca1a269b3feef86ee86835294758f84 Mon Sep 17 00:00:00 2001
+From: Oran Agra <oran@redislabs.com>
+Date: Tue, 28 Feb 2023 15:15:26 +0200
+Subject: [PATCH] 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>
+---
+ 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 d33f4522a507..26d92b92290e 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -44,8 +44,8 @@
+ #include "sha1.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]) {
+@@ -57,12 +57,24 @@
+ 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. */
+ return 0; /* no match */
+ break;
+ case '?':
+@@ -166,10 +178,17 @@
+ 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);
+ }
+
++
+ /* Convert a string representing an amount of memory into the number of
+ * bytes, so for instance memtoll("1Gb") will return 1073741824 that is
+ * (1024*1024*1024).
+
+diff --git a/tests/unit/keyspace.tcl b/tests/unit/keyspace.tcl
+index b173e0efcacc..43690d06b321 100644
+--- a/tests/unit/keyspace.tcl
++++ b/tests/unit/keyspace.tcl
+@@ -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"
++ } {}
+ }