File cve-2022-36021.patch of Package redis.31246

From 8b565570f2bc4b2e454676c9fa8a5b01eef201b1 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.

(cherry picked from commit e75f92047c22e659d49bba3a083cd0c9935f21e6)
(cherry picked from commit e8a9d3f63aebf6065d69bd0125d4b9c367f88def)
---
 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 0d48f5701..9dd7ca36f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -45,8 +45,8 @@
 #include "sha256.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]) {
@@ -58,12 +58,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 '?':
@@ -165,6 +178,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 d4e7bf51c..1617ac5aa 100644
--- a/tests/unit/keyspace.tcl
+++ b/tests/unit/keyspace.tcl
@@ -272,4 +272,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

openSUSE Build Service is sponsored by