CVE-2023-22458: Integer Overflow or Wraparound (CWE-190)
value: Redis is an in-memory database that persists on disk. Authenticated users can issue a `HRANDFIELD` or `ZRANDMEMBER` command with specially crafted arguments to trigger a denial-of-service by crashing Redis with an assertion failure. This problem affects Redis versions 6.2 or newer up to but not including 6.2.9 as well as versions 7.0 up to but not including 7.0.8. Users are advised to upgrade. There are no known workarounds for this vulnerability.
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 5.5
Published: 2023-01-20T19:15Z
Repository: https://github.com/redis/redis
Language: Other
Files changed: src/t_hash.c, src/t_zset.c, tests/unit/type/hash.tcl, tests/unit/type/zset.tcl
Commit date: 2023-01-16 13:50:27 +0200
Commit message: Fix range issues in ZRANDMEMBER and HRANDFIELD (CVE-2023-22458) (#11674)

missing range check in ZRANDMEMBER and HRANDIFLD leading to panic due
to protocol limitations
Diff: diff --git a/src/t_hash.c b/src/t_hash.c
index a2af438aee..9ebd99ccc0 100644
--- a/src/t_hash.c
+++ b/src/t_hash.c
@@ -1118,12 +1118,17 @@ void hrandfieldCommand(client *c) {
     if (c->argc >= 3) {
         if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
         if (c->argc > 4 || (c->argc == 4 && strcasecmp(c->argv[3]->ptr,"withvalues"))) {
             addReplyErrorObject(c,shared.syntaxerr);
             return;
-        } else if (c->argc == 4)
+        } else if (c->argc == 4) {
             withvalues = 1;
+            if (l < LONG_MIN/2 || l > LONG_MAX/2) {
+                addReplyError(c,"value is out of range");
+                return;
+            }
+        }
         hrandfieldWithCountCommand(c, l, withvalues);
         return;
     }
 
     /* Handle variant without <count> argument. Reply with simple bulk string */
diff --git a/src/t_zset.c b/src/t_zset.c
index 5dadd58fd2..e0d8509cf9 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -4315,12 +4315,17 @@ void zrandmemberCommand(client *c) {
     if (c->argc >= 3) {
         if (getLongFromObjectOrReply(c,c->argv[2],&l,NULL) != C_OK) return;
         if (c->argc > 4 || (c->argc == 4 && strcasecmp(c->argv[3]->ptr,"withscores"))) {
             addReplyErrorObject(c,shared.syntaxerr);
             return;
-        } else if (c->argc == 4)
+        } else if (c->argc == 4) {
             withscores = 1;
+            if (l < LONG_MIN/2 || l > LONG_MAX/2) {
+                addReplyError(c,"value is out of range");
+                return;
+            }
+        }
         zrandmemberWithCountCommand(c, l, withscores);
         return;
     }
 
     /* Handle variant without <count> argument. Reply with simple bulk string */
diff --git a/tests/unit/type/hash.tcl b/tests/unit/type/hash.tcl
index e7b9f3d855..113780ff79 100644
--- a/tests/unit/type/hash.tcl
+++ b/tests/unit/type/hash.tcl
@@ -69,10 +69,15 @@ start_server {tags {"hash"}} {
 
     test "HRANDFIELD count of 0 is handled correctly" {
         r hrandfield myhash 0
     } {}
 
+    test "HRANDFIELD count overflow" {
+        r hmset myhash a 1
+        assert_error {*value is out of range*} {r hrandfield myhash -9223372036854770000 withvalues}
+    } {}
+
     test "HRANDFIELD with <count> against non existing key" {
         r hrandfield nonexisting_key 100
     } {}
 
     # Make sure we can distinguish between an empty array and a null response
diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl
index 41898f27b0..036638510a 100644
--- a/tests/unit/type/zset.tcl
+++ b/tests/unit/type/zset.tcl
@@ -2320,10 +2320,15 @@ start_server {tags {"zset"}} {
 
     test "ZRANDMEMBER with <count> against non existing key" {
         r zrandmember nonexisting_key 100
     } {}
 
+    test "ZRANDMEMBER count overflow" {
+        r zadd myzset 0 a
+        assert_error {*value is out of range*} {r zrandmember myzset -9223372036854770000 withscores}
+    } {}
+
     # Make sure we can distinguish between an empty array and a null response
     r readraw 1
 
     test "ZRANDMEMBER count of 0 is handled correctly - emptyarray" {
         r zrandmember myzset 0

Vulnerable code:         } else if (c->argc == 4)
        } else if (c->argc == 4)
Fixed code:         } else if (c->argc == 4) {
            if (l < LONG_MIN/2 || l > LONG_MAX/2) {
                addReplyError(c,"value is out of range");
                return;
            }
        }
        } else if (c->argc == 4) {
            if (l < LONG_MIN/2 || l > LONG_MAX/2) {
                addReplyError(c,"value is out of range");
                return;
            }
        }
    test "HRANDFIELD count overflow" {
        r hmset myhash a 1
        assert_error {*value is out of range*} {r hrandfield myhash -9223372036854770000 withvalues}
    } {}

    test "ZRANDMEMBER count overflow" {
        r zadd myzset 0 a
        assert_error {*value is out of range*} {r zrandmember myzset -9223372036854770000 withscores}
    } {}