From 27e5f385c1839157574b80f2079d79bf40e32639 Mon Sep 17 00:00:00 2001
From: antirez <antirez@gmail.com>
Date: Wed, 1 Jun 2016 20:18:28 +0200
Subject: [PATCH] RDB v8: fix rdbLoadLen() return value.

---
 src/rdb.c             | 55 +++++++++++++++++++++++++++++--------------
 src/rdb.h             |  2 +-
 src/redis-check-rdb.c | 40 ++++++++++++++++++-------------
 3 files changed, 61 insertions(+), 36 deletions(-)

diff --git a/src/rdb.c b/src/rdb.c
index 049e3d96..c30bd9fb 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -128,41 +128,60 @@ int rdbSaveLen(rio *rdb, uint64_t len) {
     return nwritten;
 }
 
-/* Load an encoded length. The "isencoded" argument is set to 1 if the length
- * is not actually a length but an "encoding type". See the RDB_ENC_*
- * definitions in rdb.h for more information. */
-uint64_t rdbLoadLen(rio *rdb, int *isencoded) {
+
+/* Load an encoded length. If the loaded length is a normal length as stored
+ * with rdbSaveLen(), the read length is set to '*lenptr'. If instead the
+ * loaded length describes a special encoding that follows, then '*isencoded'
+ * is set to 1 and the encoding format is stored at '*lenptr'.
+ *
+ * See the RDB_ENC_* definitions in rdb.h for more information on special
+ * encodings.
+ *
+ * The function returns -1 on error, 0 on success. */
+int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr) {
     unsigned char buf[2];
     int type;
 
     if (isencoded) *isencoded = 0;
-    if (rioRead(rdb,buf,1) == 0) return RDB_LENERR;
+    if (rioRead(rdb,buf,1) == 0) return -1;
     type = (buf[0]&0xC0)>>6;
     if (type == RDB_ENCVAL) {
         /* Read a 6 bit encoding type. */
         if (isencoded) *isencoded = 1;
-        return buf[0]&0x3F;
+        *lenptr = buf[0]&0x3F;
     } else if (type == RDB_6BITLEN) {
         /* Read a 6 bit len. */
-        return buf[0]&0x3F;
+        *lenptr = buf[0]&0x3F;
     } else if (type == RDB_14BITLEN) {
         /* Read a 14 bit len. */
-        if (rioRead(rdb,buf+1,1) == 0) return RDB_LENERR;
-        return ((buf[0]&0x3F)<<8)|buf[1];
+        if (rioRead(rdb,buf+1,1) == 0) return -1;
+        *lenptr = ((buf[0]&0x3F)<<8)|buf[1];
     } else if (buf[0] == RDB_32BITLEN) {
         /* Read a 32 bit len. */
         uint32_t len;
-        if (rioRead(rdb,&len,4) == 0) return RDB_LENERR;
-        return ntohl(len);
+        if (rioRead(rdb,&len,4) == 0) return -1;
+        *lenptr = ntohl(len);
     } else if (buf[0] == RDB_64BITLEN) {
         /* Read a 64 bit len. */
         uint64_t len;
-        if (rioRead(rdb,&len,8) == 0) return RDB_LENERR;
-        return ntohu64(len);
+        if (rioRead(rdb,&len,8) == 0) return -1;
+        *lenptr = ntohu64(len);
     } else {
         rdbExitReportCorruptRDB("Unknown length encoding in rdbLoadLen()");
-        return 0; /* Never reached. */
+        return -1; /* Never reached. */
     }
+    return 0;
+}
+
+/* This is like rdbLoadLenByRef() but directly returns the value read
+ * from the RDB stream, signaling an error by returning RDB_LENERR
+ * (since it is a too large count to be applicable in any Redis data
+ * structure). */
+uint64_t rdbLoadLen(rio *rdb, int *isencoded) {
+    uint64_t len;
+
+    if (rdbLoadLenByRef(rdb,isencoded,&len) == -1) return RDB_LENERR;
+    return len;
 }
 
 /* Encodes the "value" argument as integer when it fits in the supported ranges
@@ -299,7 +318,7 @@ ssize_t rdbSaveLzfStringObject(rio *rdb, unsigned char *s, size_t len) {
 void *rdbLoadLzfStringObject(rio *rdb, int flags) {
     int plain = flags & RDB_LOAD_PLAIN;
     int sds = flags & RDB_LOAD_SDS;
-    unsigned int len, clen;
+    uint64_t len, clen;
     unsigned char *c = NULL;
     char *val = NULL;
 
@@ -414,7 +433,7 @@ void *rdbGenericLoadStringObject(rio *rdb, int flags) {
     int plain = flags & RDB_LOAD_PLAIN;
     int sds = flags & RDB_LOAD_SDS;
     int isencoded;
-    uint32_t len;
+    uint64_t len;
 
     len = rdbLoadLen(rdb,&isencoded);
     if (isencoded) {
@@ -1291,7 +1310,7 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
 }
 
 int rdbLoad(char *filename) {
-    uint32_t dbid;
+    uint64_t dbid;
     int type, rdbver;
     redisDb *db = server.db+0;
     char buf[1024];
@@ -1364,7 +1383,7 @@ int rdbLoad(char *filename) {
         } else if (type == RDB_OPCODE_RESIZEDB) {
             /* RESIZEDB: Hint about the size of the keys in the currently
              * selected data base, in order to avoid useless rehashing. */
-            uint32_t db_size, expires_size;
+            uint64_t db_size, expires_size;
             if ((db_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
                 goto eoferr;
             if ((expires_size = rdbLoadLen(&rdb,NULL)) == RDB_LENERR)
diff --git a/src/rdb.h b/src/rdb.h
index d9025612..7ef6782d 100644
--- a/src/rdb.h
+++ b/src/rdb.h
@@ -59,7 +59,7 @@
 #define RDB_32BITLEN 0x80
 #define RDB_64BITLEN 0x81
 #define RDB_ENCVAL 3
-#define RDB_LENERR UINT_MAX
+#define RDB_LENERR UINT64_MAX
 
 /* When a length of a string object stored on disk has the first two bits
  * set, the remaining six bits specify a special encoding for the object
diff --git a/src/redis-check-rdb.c b/src/redis-check-rdb.c
index 7bb93b60..1e34a62c 100644
--- a/src/redis-check-rdb.c
+++ b/src/redis-check-rdb.c
@@ -171,7 +171,7 @@ static int processTime(int type) {
     return 0;
 }
 
-static uint32_t loadLength(int *isencoded) {
+static uint64_t loadLength(int *isencoded) {
     unsigned char buf[2];
     uint32_t len;
     int type;
@@ -190,10 +190,16 @@ static uint32_t loadLength(int *isencoded) {
         /* Read a 14 bit len */
         if (!readBytes(buf+1,1)) return RDB_LENERR;
         return ((buf[0] & 0x3F) << 8) | buf[1];
-    } else {
+    } else if (buf[0] == RDB_32BITLEN) {
         /* Read a 32 bit len */
         if (!readBytes(&len, 4)) return RDB_LENERR;
-        return (unsigned int)ntohl(len);
+        return ntohl(len);
+    } else if (buf[0] == RDB_64BITLEN) {
+        /* Read a 64 bit len */
+        if (!readBytes(&len, 8)) return RDB_LENERR;
+        return ntohu64(len);
+    } else {
+        return RDB_LENERR;
     }
 }
 
@@ -230,7 +236,7 @@ static char *loadIntegerObject(int enctype) {
 }
 
 static char* loadLzfStringObject() {
-    unsigned int slen, clen;
+    uint64_t slen, clen;
     char *c, *s;
 
     if ((clen = loadLength(NULL)) == RDB_LENERR) return NULL;
@@ -254,9 +260,9 @@ static char* loadLzfStringObject() {
 
 /* returns NULL when not processable, char* when valid */
 static char* loadStringObject() {
-    uint32_t offset = CURR_OFFSET;
+    uint64_t offset = CURR_OFFSET;
+    uint64_t len;
     int isencoded;
-    uint32_t len;
 
     len = loadLength(&isencoded);
     if (isencoded) {
@@ -269,7 +275,7 @@ static char* loadStringObject() {
             return loadLzfStringObject();
         default:
             /* unknown encoding */
-            SHIFT_ERROR(offset, "Unknown string encoding (0x%02x)", len);
+            SHIFT_ERROR(offset, "Unknown string encoding (0x%02llx)", len);
             return NULL;
         }
     }
@@ -344,8 +350,8 @@ static int processDoubleValue(double** store) {
 }
 
 static int loadPair(entry *e) {
-    uint32_t offset = CURR_OFFSET;
-    uint32_t i;
+    uint64_t offset = CURR_OFFSET;
+    uint64_t i;
 
     /* read key first */
     char *key;
@@ -356,7 +362,7 @@ static int loadPair(entry *e) {
         return 0;
     }
 
-    uint32_t length = 0;
+    uint64_t length = 0;
     if (e->type == RDB_TYPE_LIST ||
         e->type == RDB_TYPE_SET  ||
         e->type == RDB_TYPE_ZSET ||
@@ -384,7 +390,7 @@ static int loadPair(entry *e) {
         for (i = 0; i < length; i++) {
             offset = CURR_OFFSET;
             if (!processStringObject(NULL)) {
-                SHIFT_ERROR(offset, "Error reading element at index %d (length: %d)", i, length);
+                SHIFT_ERROR(offset, "Error reading element at index %llu (length: %llu)", i, length);
                 return 0;
             }
         }
@@ -393,12 +399,12 @@ static int loadPair(entry *e) {
         for (i = 0; i < length; i++) {
             offset = CURR_OFFSET;
             if (!processStringObject(NULL)) {
-                SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
+                SHIFT_ERROR(offset, "Error reading element key at index %llu (length: %llu)", i, length);
                 return 0;
             }
             offset = CURR_OFFSET;
             if (!processDoubleValue(NULL)) {
-                SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
+                SHIFT_ERROR(offset, "Error reading element value at index %llu (length: %llu)", i, length);
                 return 0;
             }
         }
@@ -407,12 +413,12 @@ static int loadPair(entry *e) {
         for (i = 0; i < length; i++) {
             offset = CURR_OFFSET;
             if (!processStringObject(NULL)) {
-                SHIFT_ERROR(offset, "Error reading element key at index %d (length: %d)", i, length);
+                SHIFT_ERROR(offset, "Error reading element key at index %llu (length: %llu)", i, length);
                 return 0;
             }
             offset = CURR_OFFSET;
             if (!processStringObject(NULL)) {
-                SHIFT_ERROR(offset, "Error reading element value at index %d (length: %d)", i, length);
+                SHIFT_ERROR(offset, "Error reading element value at index %llu (length: %llu)", i, length);
                 return 0;
             }
         }
@@ -428,7 +434,7 @@ static int loadPair(entry *e) {
 
 static entry loadEntry() {
     entry e = { NULL, -1, 0 };
-    uint32_t length, offset[4];
+    uint64_t length, offset[4];
 
     /* reset error container */
     errors.level = 0;
@@ -445,7 +451,7 @@ static entry loadEntry() {
             return e;
         }
         if (length > 63) {
-            SHIFT_ERROR(offset[1], "Database number out of range (%d)", length);
+            SHIFT_ERROR(offset[1], "Database number out of range (%llu)", length);
             return e;
         }
     } else if (e.type == RDB_OPCODE_EOF) {