diff --git a/.gitignore b/.gitignore index 1e05039..a3f9d7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/redis-6.0.9.tar.gz +SOURCES/redis-6.2.7.tar.gz SOURCES/redis-doc-8d4bf9b.tar.gz diff --git a/.redis.metadata b/.redis.metadata index 3c8e9d8..af3ea94 100644 --- a/.redis.metadata +++ b/.redis.metadata @@ -1,2 +1,2 @@ -416ab41ac74be959ad4192462eecaa8ba9a6d3b7 SOURCES/redis-6.0.9.tar.gz +b01ef3f117c9815dea41bf2609e489a03c3a5ab1 SOURCES/redis-6.2.7.tar.gz 45ec7c3b4a034891252507febace7e25ee64b4d9 SOURCES/redis-doc-8d4bf9b.tar.gz diff --git a/SOURCES/0002-install-redis-check-rdb-as-a-symlink-instead-of-dupl.patch b/SOURCES/0002-install-redis-check-rdb-as-a-symlink-instead-of-dupl.patch deleted file mode 100644 index d43be3b..0000000 --- a/SOURCES/0002-install-redis-check-rdb-as-a-symlink-instead-of-dupl.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 79ed52edf84676786e5817cddb8914c5925144c7 Mon Sep 17 00:00:00 2001 -From: Remi Collet -Date: Fri, 9 Sep 2016 17:23:27 +0200 -Subject: [PATCH 2/3] install redis-check-rdb as a symlink instead of - duplicating the binary - ---- - src/Makefile | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/Makefile b/src/Makefile -index 2a68649..585c95b 100644 ---- a/src/Makefile -+++ b/src/Makefile -@@ -307,9 +307,9 @@ install: all - $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) - $(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) - $(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) -- $(REDIS_INSTALL) $(REDIS_CHECK_RDB_NAME) $(INSTALL_BIN) -- $(REDIS_INSTALL) $(REDIS_CHECK_AOF_NAME) $(INSTALL_BIN) - @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME) -+ @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME) -+ @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME) - - uninstall: - rm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)} --- -2.24.1 - diff --git a/SOURCES/redis-CVE-2021-26477.patch b/SOURCES/redis-CVE-2021-26477.patch deleted file mode 100644 index 6def3db..0000000 --- a/SOURCES/redis-CVE-2021-26477.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 394614a5f91d88380f480c4610926a865b5b0f16 Mon Sep 17 00:00:00 2001 -From: Oran Agra -Date: Mon, 3 May 2021 08:32:31 +0300 -Subject: [PATCH] Fix integer overflow in STRALGO LCS (CVE-2021-29477) - -An integer overflow bug in Redis version 6.0 or newer could be exploited using -the STRALGO LCS command to corrupt the heap and potentially result with remote -code execution. - -(cherry picked from commit f0c5f920d0f88bd8aa376a2c05af4902789d1ef9) ---- - src/t_string.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/t_string.c b/src/t_string.c -index 4886f7e44388..5310a297db16 100644 ---- a/src/t_string.c -+++ b/src/t_string.c -@@ -576,7 +576,7 @@ void stralgoLCS(client *c) { - /* Setup an uint32_t array to store at LCS[i,j] the length of the - * LCS A0..i-1, B0..j-1. Note that we have a linear array here, so - * we index it as LCS[j+(blen+1)*j] */ -- uint32_t *lcs = zmalloc((alen+1)*(blen+1)*sizeof(uint32_t)); -+ uint32_t *lcs = zmalloc((size_t)(alen+1)*(blen+1)*sizeof(uint32_t)); - #define LCS(A,B) lcs[(B)+((A)*(blen+1))] - - /* Start building the LCS table. */ diff --git a/SOURCES/redis-CVE-2021-32626.patch b/SOURCES/redis-CVE-2021-32626.patch deleted file mode 100644 index d81b4e8..0000000 --- a/SOURCES/redis-CVE-2021-32626.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 666ed7facf4524bf6d19b11b20faa2cf93fdf591 Mon Sep 17 00:00:00 2001 -From: "meir@redislabs.com" -Date: Sun, 13 Jun 2021 14:27:18 +0300 -Subject: [PATCH] Fix invalid memory write on lua stack overflow - {CVE-2021-32626} -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -When LUA call our C code, by default, the LUA stack has room for 20 -elements. In most cases, this is more than enough but sometimes it's not -and the caller must verify the LUA stack size before he pushes elements. - -On 3 places in the code, there was no verification of the LUA stack size. -On specific inputs this missing verification could have lead to invalid -memory write: -1. On 'luaReplyToRedisReply', one might return a nested reply that will - explode the LUA stack. -2. On 'redisProtocolToLuaType', the Redis reply might be deep enough -   to explode the LUA stack (notice that currently there is no such -   command in Redis that returns such a nested reply, but modules might -   do it) -3. On 'ldbRedis', one might give a command with enough arguments to -   explode the LUA stack (all the arguments will be pushed to the LUA -   stack) - -This commit is solving all those 3 issues by calling 'lua_checkstack' and -verify that there is enough room in the LUA stack to push elements. In -case 'lua_checkstack' returns an error (there is not enough room in the -LUA stack and it's not possible to increase the stack), we will do the -following: -1. On 'luaReplyToRedisReply', we will return an error to the user. -2. On 'redisProtocolToLuaType' we will exit with panic (we assume this - scenario is rare because it can only happen with a module). -3. On 'ldbRedis', we return an error. ---- - src/scripting.c | 41 +++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 41 insertions(+) - -diff --git a/src/scripting.c b/src/scripting.c -index dea5f516561..afa6adb0c47 100644 ---- a/src/scripting.c -+++ b/src/scripting.c -@@ -128,6 +128,16 @@ void sha1hex(char *digest, char *script, size_t len) { - */ - - char *redisProtocolToLuaType(lua_State *lua, char* reply) { -+ -+ if (!lua_checkstack(lua, 5)) { -+ /* -+ * Increase the Lua stack if needed, to make sure there is enough room -+ * to push 5 elements to the stack. On failure, exit with panic. -+         * Notice that we need, in the worst case, 5 elements because redisProtocolToLuaType_Aggregate -+         * might push 5 elements to the Lua stack.*/ -+ serverPanic("lua stack limit reach when parsing redis.call reply"); -+ } -+ - char *p = reply; - - switch(*p) { -@@ -220,6 +230,11 @@ char *redisProtocolToLuaType_Aggregate(lua_State *lua, char *reply, int atype) { - if (atype == '%') { - p = redisProtocolToLuaType(lua,p); - } else { -+ if (!lua_checkstack(lua, 1)) { -+ /* Notice that here we need to check the stack again because the recursive -+ * call to redisProtocolToLuaType might have use the room allocated in the stack */ -+ serverPanic("lua stack limit reach when parsing redis.call reply"); -+ } - lua_pushboolean(lua,1); - } - lua_settable(lua,-3); -@@ -339,6 +354,17 @@ void luaSortArray(lua_State *lua) { - /* Reply to client 'c' converting the top element in the Lua stack to a - * Redis reply. As a side effect the element is consumed from the stack. */ - void luaReplyToRedisReply(client *c, lua_State *lua) { -+ -+ if (!lua_checkstack(lua, 4)) { -+ /* Increase the Lua stack if needed to make sure there is enough room -+ * to push 4 elements to the stack. On failure, return error. -+         * Notice that we need, in the worst case, 4 elements because returning a map might -+ * require push 4 elements to the Lua stack.*/ -+ addReplyErrorFormat(c, "reached lua stack limit"); -+ lua_pop(lua,1); // pop the element from the stack -+ return; -+ } -+ - int t = lua_type(lua,-1); - - switch(t) { -@@ -362,6 +388,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { - * field. */ - - /* Handle error reply. */ -+ // we took care of the stack size on function start - lua_pushstring(lua,"err"); - lua_gettable(lua,-2); - t = lua_type(lua,-1); -@@ -407,6 +434,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { - if (t == LUA_TTABLE) { - int maplen = 0; - void *replylen = addReplyDeferredLen(c); -+ /* we took care of the stack size on function start */ - lua_pushnil(lua); /* Use nil to start iteration. */ - while (lua_next(lua,-2)) { - /* Stack now: table, key, value */ -@@ -429,6 +457,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { - if (t == LUA_TTABLE) { - int setlen = 0; - void *replylen = addReplyDeferredLen(c); -+ /* we took care of the stack size on function start */ - lua_pushnil(lua); /* Use nil to start iteration. */ - while (lua_next(lua,-2)) { - /* Stack now: table, key, true */ -@@ -448,6 +477,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { - void *replylen = addReplyDeferredLen(c); - int j = 1, mbulklen = 0; - while(1) { -+ /* we took care of the stack size on function start */ - lua_pushnumber(lua,j++); - lua_gettable(lua,-2); - t = lua_type(lua,-1); -@@ -2506,6 +2536,17 @@ void ldbEval(lua_State *lua, sds *argv, int argc) { - void ldbRedis(lua_State *lua, sds *argv, int argc) { - int j, saved_rc = server.lua_replicate_commands; - -+ if (!lua_checkstack(lua, argc + 1)) { -+ /* Increase the Lua stack if needed to make sure there is enough room -+ * to push 'argc + 1' elements to the stack. On failure, return error. -+         * Notice that we need, in worst case, 'argc + 1' elements because we push all the arguments -+         * given by the user (without the first argument) and we also push the 'redis' global table and -+         * 'redis.call' function so: -+         * (1 (redis table)) + (1 (redis.call function)) + (argc - 1 (all arguments without the first)) = argc + 1*/ -+ ldbLogRedisReply("max lua stack reached"); -+ return; -+ } -+ - lua_getglobal(lua,"redis"); - lua_pushstring(lua,"call"); - lua_gettable(lua,-2); /* Stack: redis, redis.call */ diff --git a/SOURCES/redis-CVE-2021-32627.patch b/SOURCES/redis-CVE-2021-32627.patch deleted file mode 100644 index 95d40db..0000000 --- a/SOURCES/redis-CVE-2021-32627.patch +++ /dev/null @@ -1,781 +0,0 @@ -From f6a40570fa63d5afdd596c78083d754081d80ae3 Mon Sep 17 00:00:00 2001 -From: Oran Agra -Date: Thu, 3 Jun 2021 12:10:02 +0300 -Subject: [PATCH] Fix ziplist and listpack overflows and truncations - (CVE-2021-32627, CVE-2021-32628) - -- fix possible heap corruption in ziplist and listpack resulting by trying to - allocate more than the maximum size of 4GB. -- prevent ziplist (hash and zset) from reaching size of above 1GB, will be - converted to HT encoding, that's not a useful size. -- prevent listpack (stream) from reaching size of above 1GB. -- XADD will start a new listpack if the new record may cause the previous - listpack to grow over 1GB. -- XADD will respond with an error if a single stream record is over 1GB -- List type (ziplist in quicklist) was truncating strings that were over 4GB, - now it'll respond with an error. ---- - src/geo.c | 5 +- - src/listpack.c | 2 +- - src/quicklist.c | 17 ++++- - src/rdb.c | 36 ++++++--- - src/server.h | 2 +- - src/t_hash.c | 13 +++- - src/t_list.c | 30 ++++++++ - src/t_stream.c | 48 +++++++++--- - src/t_zset.c | 43 +++++++---- - src/ziplist.c | 17 ++++- - src/ziplist.h | 1 + - tests/support/util.tcl | 18 ++++- - tests/unit/violations.tcl | 156 ++++++++++++++++++++++++++++++++++++++ - 13 files changed, 339 insertions(+), 49 deletions(-) - create mode 100644 tests/unit/violations.tcl - -diff --git a/src/geo.c b/src/geo.c -index 5c505441486c..a8710cd8b3ae 100644 ---- a/src/geo.c -+++ b/src/geo.c -@@ -635,7 +635,7 @@ void georadiusGeneric(client *c, int flags) { - robj *zobj; - zset *zs; - int i; -- size_t maxelelen = 0; -+ size_t maxelelen = 0, totelelen = 0; - - if (returned_items) { - zobj = createZsetObject(); -@@ -650,13 +650,14 @@ void georadiusGeneric(client *c, int flags) { - size_t elelen = sdslen(gp->member); - - if (maxelelen < elelen) maxelelen = elelen; -+ totelelen += elelen; - znode = zslInsert(zs->zsl,score,gp->member); - serverAssert(dictAdd(zs->dict,gp->member,&znode->score) == DICT_OK); - gp->member = NULL; - } - - if (returned_items) { -- zsetConvertToZiplistIfNeeded(zobj,maxelelen); -+ zsetConvertToZiplistIfNeeded(zobj,maxelelen,totelelen); - setKey(c,c->db,storekey,zobj); - decrRefCount(zobj); - notifyKeyspaceEvent(NOTIFY_ZSET,"georadiusstore",storekey, -diff --git a/src/listpack.c b/src/listpack.c -index f8c34429ec1e..6c111e83e029 100644 ---- a/src/listpack.c -+++ b/src/listpack.c -@@ -283,7 +283,7 @@ int lpEncodeGetType(unsigned char *ele, uint32_t size, unsigned char *intenc, ui - } else { - if (size < 64) *enclen = 1+size; - else if (size < 4096) *enclen = 2+size; -- else *enclen = 5+size; -+ else *enclen = 5+(uint64_t)size; - return LP_ENCODING_STRING; - } - } -diff --git a/src/quicklist.c b/src/quicklist.c -index 52e3988f59b1..c4bf5274eb05 100644 ---- a/src/quicklist.c -+++ b/src/quicklist.c -@@ -29,6 +29,7 @@ - */ - - #include /* for memcpy */ -+#include "redisassert.h" - #include "quicklist.h" - #include "zmalloc.h" - #include "ziplist.h" -@@ -43,11 +44,16 @@ - #define REDIS_STATIC static - #endif - --/* Optimization levels for size-based filling */ -+/* Optimization levels for size-based filling. -+ * Note that the largest possible limit is 16k, so even if each record takes -+ * just one byte, it still won't overflow the 16 bit count field. */ - static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536}; - - /* Maximum size in bytes of any multi-element ziplist. -- * Larger values will live in their own isolated ziplists. */ -+ * Larger values will live in their own isolated ziplists. -+ * This is used only if we're limited by record count. when we're limited by -+ * size, the maximum limit is bigger, but still safe. -+ * 8k is a recommended / default size limit */ - #define SIZE_SAFETY_LIMIT 8192 - - /* Minimum ziplist size in bytes for attempting compression. */ -@@ -449,6 +455,8 @@ REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node, - unsigned int new_sz = node->sz + sz + ziplist_overhead; - if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill))) - return 1; -+ /* when we return 1 above we know that the limit is a size limit (which is -+ * safe, see comments next to optimization_level and SIZE_SAFETY_LIMIT) */ - else if (!sizeMeetsSafetyLimit(new_sz)) - return 0; - else if ((int)node->count < fill) -@@ -468,6 +476,8 @@ REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a, - unsigned int merge_sz = a->sz + b->sz - 11; - if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill))) - return 1; -+ /* when we return 1 above we know that the limit is a size limit (which is -+ * safe, see comments next to optimization_level and SIZE_SAFETY_LIMIT) */ - else if (!sizeMeetsSafetyLimit(merge_sz)) - return 0; - else if ((int)(a->count + b->count) <= fill) -@@ -487,6 +497,7 @@ REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a, - * Returns 1 if new head created. */ - int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) { - quicklistNode *orig_head = quicklist->head; -+ assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ - if (likely( - _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) { - quicklist->head->zl = -@@ -510,6 +521,7 @@ int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) { - * Returns 1 if new tail created. */ - int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) { - quicklistNode *orig_tail = quicklist->tail; -+ assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ - if (likely( - _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) { - quicklist->tail->zl = -@@ -852,6 +864,7 @@ REDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry, - int fill = quicklist->fill; - quicklistNode *node = entry->node; - quicklistNode *new_node = NULL; -+ assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ - - if (!node) { - /* we have no reference node, so let's create only node in the list */ -diff --git a/src/rdb.c b/src/rdb.c -index ecd2c0e9870a..11cc41b56240 100644 ---- a/src/rdb.c -+++ b/src/rdb.c -@@ -1561,7 +1561,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - } else if (rdbtype == RDB_TYPE_ZSET_2 || rdbtype == RDB_TYPE_ZSET) { - /* Read list/set value. */ - uint64_t zsetlen; -- size_t maxelelen = 0; -+ size_t maxelelen = 0, totelelen = 0; - zset *zs; - - if ((zsetlen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; -@@ -1598,6 +1598,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - - /* Don't care about integer-encoded strings. */ - if (sdslen(sdsele) > maxelelen) maxelelen = sdslen(sdsele); -+ totelelen += sdslen(sdsele); - - znode = zslInsert(zs->zsl,score,sdsele); - dictAdd(zs->dict,sdsele,&znode->score); -@@ -1605,8 +1606,11 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - - /* Convert *after* loading, since sorted sets are not stored ordered. */ - if (zsetLength(o) <= server.zset_max_ziplist_entries && -- maxelelen <= server.zset_max_ziplist_value) -- zsetConvert(o,OBJ_ENCODING_ZIPLIST); -+ maxelelen <= server.zset_max_ziplist_value && -+ ziplistSafeToAdd(NULL, totelelen)) -+ { -+ zsetConvert(o,OBJ_ENCODING_ZIPLIST); -+ } - } else if (rdbtype == RDB_TYPE_HASH) { - uint64_t len; - int ret; -@@ -1635,21 +1639,25 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - return NULL; - } - -- /* Add pair to ziplist */ -- o->ptr = ziplistPush(o->ptr, (unsigned char*)field, -- sdslen(field), ZIPLIST_TAIL); -- o->ptr = ziplistPush(o->ptr, (unsigned char*)value, -- sdslen(value), ZIPLIST_TAIL); -- - /* Convert to hash table if size threshold is exceeded */ - if (sdslen(field) > server.hash_max_ziplist_value || -- sdslen(value) > server.hash_max_ziplist_value) -+ sdslen(value) > server.hash_max_ziplist_value || -+ !ziplistSafeToAdd(o->ptr, sdslen(field)+sdslen(value))) - { -- sdsfree(field); -- sdsfree(value); - hashTypeConvert(o, OBJ_ENCODING_HT); -+ ret = dictAdd((dict*)o->ptr, field, value); -+ if (ret == DICT_ERR) { -+ rdbExitReportCorruptRDB("Duplicate hash fields detected"); -+ } - break; - } -+ -+ /* Add pair to ziplist */ -+ o->ptr = ziplistPush(o->ptr, (unsigned char*)field, -+ sdslen(field), ZIPLIST_TAIL); -+ o->ptr = ziplistPush(o->ptr, (unsigned char*)value, -+ sdslen(value), ZIPLIST_TAIL); -+ - sdsfree(field); - sdsfree(value); - } -@@ -1726,6 +1734,10 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - while ((zi = zipmapNext(zi, &fstr, &flen, &vstr, &vlen)) != NULL) { - if (flen > maxlen) maxlen = flen; - if (vlen > maxlen) maxlen = vlen; -+ if (!ziplistSafeToAdd(zl, (size_t)flen + vlen)) { -+ rdbExitReportCorruptRDB("Hash zipmap too big (%u)", flen); -+ } -+ - zl = ziplistPush(zl, fstr, flen, ZIPLIST_TAIL); - zl = ziplistPush(zl, vstr, vlen, ZIPLIST_TAIL); - } -diff --git a/src/server.h b/src/server.h -index 530355421a8d..38774bbc2fa7 100644 ---- a/src/server.h -+++ b/src/server.h -@@ -1999,7 +1999,7 @@ unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range); - unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range); - unsigned long zsetLength(const robj *zobj); - void zsetConvert(robj *zobj, int encoding); --void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen); -+void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen); - int zsetScore(robj *zobj, sds member, double *score); - unsigned long zslGetRank(zskiplist *zsl, double score, sds o); - int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore); -diff --git a/src/t_hash.c b/src/t_hash.c -index 8e79432a4fbd..3cdfdd169abf 100644 ---- a/src/t_hash.c -+++ b/src/t_hash.c -@@ -39,17 +39,22 @@ - * as their string length can be queried in constant time. */ - void hashTypeTryConversion(robj *o, robj **argv, int start, int end) { - int i; -+ size_t sum = 0; - - if (o->encoding != OBJ_ENCODING_ZIPLIST) return; - - for (i = start; i <= end; i++) { -- if (sdsEncodedObject(argv[i]) && -- sdslen(argv[i]->ptr) > server.hash_max_ziplist_value) -- { -+ if (!sdsEncodedObject(argv[i])) -+ continue; -+ size_t len = sdslen(argv[i]->ptr); -+ if (len > server.hash_max_ziplist_value) { - hashTypeConvert(o, OBJ_ENCODING_HT); -- break; -+ return; - } -+ sum += len; - } -+ if (!ziplistSafeToAdd(o->ptr, sum)) -+ hashTypeConvert(o, OBJ_ENCODING_HT); - } - - /* Get the value from a ziplist encoded hash, identified by field. -diff --git a/src/t_list.c b/src/t_list.c -index 4f0bd7b81a5d..621b6932759e 100644 ---- a/src/t_list.c -+++ b/src/t_list.c -@@ -29,6 +29,8 @@ - - #include "server.h" - -+#define LIST_MAX_ITEM_SIZE ((1ull<<32)-1024) -+ - /*----------------------------------------------------------------------------- - * List API - *----------------------------------------------------------------------------*/ -@@ -196,6 +198,14 @@ void listTypeConvert(robj *subject, int enc) { - - void pushGenericCommand(client *c, int where) { - int j, pushed = 0; -+ -+ for (j = 2; j < c->argc; j++) { -+ if (sdslen(c->argv[j]->ptr) > LIST_MAX_ITEM_SIZE) { -+ addReplyError(c, "Element too large"); -+ return; -+ } -+ } -+ - robj *lobj = lookupKeyWrite(c->db,c->argv[1]); - - if (lobj && lobj->type != OBJ_LIST) { -@@ -277,6 +287,11 @@ void linsertCommand(client *c) { - return; - } - -+ if (sdslen(c->argv[4]->ptr) > LIST_MAX_ITEM_SIZE) { -+ addReplyError(c, "Element too large"); -+ return; -+ } -+ - if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || - checkType(c,subject,OBJ_LIST)) return; - -@@ -344,6 +359,11 @@ void lsetCommand(client *c) { - long index; - robj *value = c->argv[3]; - -+ if (sdslen(value->ptr) > LIST_MAX_ITEM_SIZE) { -+ addReplyError(c, "Element too large"); -+ return; -+ } -+ - if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)) - return; - -@@ -510,6 +530,11 @@ void lposCommand(client *c) { - int direction = LIST_TAIL; - long rank = 1, count = -1, maxlen = 0; /* Count -1: option not given. */ - -+ if (sdslen(ele->ptr) > LIST_MAX_ITEM_SIZE) { -+ addReplyError(c, "Element too large"); -+ return; -+ } -+ - /* Parse the optional arguments. */ - for (int j = 3; j < c->argc; j++) { - char *opt = c->argv[j]->ptr; -@@ -610,6 +635,11 @@ void lremCommand(client *c) { - long toremove; - long removed = 0; - -+ if (sdslen(obj->ptr) > LIST_MAX_ITEM_SIZE) { -+ addReplyError(c, "Element too large"); -+ return; -+ } -+ - if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK)) - return; - -diff --git a/src/t_stream.c b/src/t_stream.c -index 43b67e8826ca..30411388697f 100644 ---- a/src/t_stream.c -+++ b/src/t_stream.c -@@ -40,6 +40,12 @@ - #define STREAM_ITEM_FLAG_DELETED (1<<0) /* Entry is deleted. Skip it. */ - #define STREAM_ITEM_FLAG_SAMEFIELDS (1<<1) /* Same fields as master entry. */ - -+/* Don't let listpacks grow too big, even if the user config allows it. -+ * doing so can lead to an overflow (trying to store more than 32bit length -+ * into the listpack header), or actually an assertion since lpInsert -+ * will return NULL. */ -+#define STREAM_LISTPACK_MAX_SIZE (1<<30) -+ - void streamFreeCG(streamCG *cg); - void streamFreeNACK(streamNACK *na); - size_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer); -@@ -191,8 +197,11 @@ int streamCompareID(streamID *a, streamID *b) { - * - * The function returns C_OK if the item was added, this is always true - * if the ID was generated by the function. However the function may return -- * C_ERR if an ID was given via 'use_id', but adding it failed since the -- * current top ID is greater or equal. */ -+ * C_ERR in several cases: -+ * 1. If an ID was given via 'use_id', but adding it failed since the -+ * current top ID is greater or equal. errno will be set to EDOM. -+ * 2. If a size of a single element or the sum of the elements is too big to -+ * be stored into the stream. errno will be set to ERANGE. */ - int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) { - - /* Generate the new entry ID. */ -@@ -206,7 +215,23 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_ - * or return an error. Automatically generated IDs might - * overflow (and wrap-around) when incrementing the sequence - part. */ -- if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR; -+ if (streamCompareID(&id,&s->last_id) <= 0) { -+ errno = EDOM; -+ return C_ERR; -+ } -+ -+ /* Avoid overflow when trying to add an element to the stream (listpack -+ * can only host up to 32bit length sttrings, and also a total listpack size -+ * can't be bigger than 32bit length. */ -+ size_t totelelen = 0; -+ for (int64_t i = 0; i < numfields*2; i++) { -+ sds ele = argv[i]->ptr; -+ totelelen += sdslen(ele); -+ } -+ if (totelelen > STREAM_LISTPACK_MAX_SIZE) { -+ errno = ERANGE; -+ return C_ERR; -+ } - - /* Add the new entry. */ - raxIterator ri; -@@ -265,9 +290,10 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_ - * if we need to switch to the next one. 'lp' will be set to NULL if - * the current node is full. */ - if (lp != NULL) { -- if (server.stream_node_max_bytes && -- lp_bytes >= server.stream_node_max_bytes) -- { -+ size_t node_max_bytes = server.stream_node_max_bytes; -+ if (node_max_bytes == 0 || node_max_bytes > STREAM_LISTPACK_MAX_SIZE) -+ node_max_bytes = STREAM_LISTPACK_MAX_SIZE; -+ if (lp_bytes + totelelen >= node_max_bytes) { - lp = NULL; - } else if (server.stream_node_max_entries) { - int64_t count = lpGetInteger(lpFirst(lp)); -@@ -1267,11 +1293,13 @@ void xaddCommand(client *c) { - - /* Append using the low level function and return the ID. */ - if (streamAppendItem(s,c->argv+field_pos,(c->argc-field_pos)/2, -- &id, id_given ? &id : NULL) -- == C_ERR) -+ &id, id_given ? &id : NULL) == C_ERR) - { -- addReplyError(c,"The ID specified in XADD is equal or smaller than the " -- "target stream top item"); -+ if (errno == EDOM) -+ addReplyError(c,"The ID specified in XADD is equal or smaller than " -+ "the target stream top item"); -+ else -+ addReplyError(c,"Elements are too large to be stored"); - return; - } - addReplyStreamID(c,&id); -diff --git a/src/t_zset.c b/src/t_zset.c -index d0ffe2f8b20a..b44f9551cc33 100644 ---- a/src/t_zset.c -+++ b/src/t_zset.c -@@ -1238,15 +1238,18 @@ void zsetConvert(robj *zobj, int encoding) { - } - - /* Convert the sorted set object into a ziplist if it is not already a ziplist -- * and if the number of elements and the maximum element size is within the -- * expected ranges. */ --void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) { -+ * and if the number of elements and the maximum element size and total elements size -+ * are within the expected ranges. */ -+void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen) { - if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return; - zset *zset = zobj->ptr; - - if (zset->zsl->length <= server.zset_max_ziplist_entries && -- maxelelen <= server.zset_max_ziplist_value) -- zsetConvert(zobj,OBJ_ENCODING_ZIPLIST); -+ maxelelen <= server.zset_max_ziplist_value && -+ ziplistSafeToAdd(NULL, totelelen)) -+ { -+ zsetConvert(zobj,OBJ_ENCODING_ZIPLIST); -+ } - } - - /* Return (by reference) the score of the specified member of the sorted set -@@ -1355,20 +1358,28 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) { - } - return 1; - } else if (!xx) { -- /* Optimize: check if the element is too large or the list -+ /* check if the element is too large or the list - * becomes too long *before* executing zzlInsert. */ -- zobj->ptr = zzlInsert(zobj->ptr,ele,score); -- if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries || -- sdslen(ele) > server.zset_max_ziplist_value) -+ if (zzlLength(zobj->ptr)+1 > server.zset_max_ziplist_entries || -+ sdslen(ele) > server.zset_max_ziplist_value || -+ !ziplistSafeToAdd(zobj->ptr, sdslen(ele))) -+ { - zsetConvert(zobj,OBJ_ENCODING_SKIPLIST); -- if (newscore) *newscore = score; -- *flags |= ZADD_ADDED; -- return 1; -+ } else { -+ zobj->ptr = zzlInsert(zobj->ptr,ele,score); -+ if (newscore) *newscore = score; -+ *flags |= ZADD_ADDED; -+ return 1; -+ } - } else { - *flags |= ZADD_NOP; - return 1; - } -- } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { -+ } -+ -+ /* Note that the above block handling ziplist would have either returned or -+ * converted the key to skiplist. */ -+ if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { - zset *zs = zobj->ptr; - zskiplistNode *znode; - dictEntry *de; -@@ -2180,7 +2191,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) { - zsetopsrc *src; - zsetopval zval; - sds tmp; -- size_t maxelelen = 0; -+ size_t maxelelen = 0, totelelen = 0; - robj *dstobj; - zset *dstzset; - zskiplistNode *znode; -@@ -2304,6 +2315,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) { - tmp = zuiNewSdsFromValue(&zval); - znode = zslInsert(dstzset->zsl,score,tmp); - dictAdd(dstzset->dict,tmp,&znode->score); -+ totelelen += sdslen(tmp); - if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp); - } - } -@@ -2340,6 +2352,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) { - /* Remember the longest single element encountered, - * to understand if it's possible to convert to ziplist - * at the end. */ -+ totelelen += sdslen(tmp); - if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp); - /* Update the element with its initial score. */ - dictSetKey(accumulator, de, tmp); -@@ -2380,7 +2393,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) { - if (dbDelete(c->db,dstkey)) - touched = 1; - if (dstzset->zsl->length) { -- zsetConvertToZiplistIfNeeded(dstobj,maxelelen); -+ zsetConvertToZiplistIfNeeded(dstobj,maxelelen,totelelen); - dbAdd(c->db,dstkey,dstobj); - addReplyLongLong(c,zsetLength(dstobj)); - signalModifiedKey(c,c->db,dstkey); -diff --git a/src/ziplist.c b/src/ziplist.c -index 5933d19151b0..582eed2c9b4a 100644 ---- a/src/ziplist.c -+++ b/src/ziplist.c -@@ -265,6 +265,17 @@ - ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \ - } - -+/* Don't let ziplists grow over 1GB in any case, don't wanna risk overflow in -+ * zlbytes*/ -+#define ZIPLIST_MAX_SAFETY_SIZE (1<<30) -+int ziplistSafeToAdd(unsigned char* zl, size_t add) { -+ size_t len = zl? ziplistBlobLen(zl): 0; -+ if (len + add > ZIPLIST_MAX_SAFETY_SIZE) -+ return 0; -+ return 1; -+} -+ -+ - /* We use this function to receive information about a ziplist entry. - * Note that this is not how the data is actually encoded, is just what we - * get filled by a function in order to operate more easily. */ -@@ -586,7 +597,8 @@ unsigned char *ziplistNew(void) { - } - - /* Resize the ziplist. */ --unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { -+unsigned char *ziplistResize(unsigned char *zl, size_t len) { -+ assert(len < UINT32_MAX); - zl = zrealloc(zl,len); - ZIPLIST_BYTES(zl) = intrev32ifbe(len); - zl[len-1] = ZIP_END; -@@ -898,6 +910,9 @@ unsigned char *ziplistMerge(unsigned char **first, unsigned char **second) { - /* Combined zl length should be limited within UINT16_MAX */ - zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX; - -+ /* larger values can't be stored into ZIPLIST_BYTES */ -+ assert(zlbytes < UINT32_MAX); -+ - /* Save offset positions before we start ripping memory apart. */ - size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first)); - size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second)); -diff --git a/src/ziplist.h b/src/ziplist.h -index 964a47f6dc29..f6ba6c8be47d 100644 ---- a/src/ziplist.h -+++ b/src/ziplist.h -@@ -49,6 +49,7 @@ unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int v - unsigned int ziplistLen(unsigned char *zl); - size_t ziplistBlobLen(unsigned char *zl); - void ziplistRepr(unsigned char *zl); -+int ziplistSafeToAdd(unsigned char* zl, size_t add); - - #ifdef REDIS_TEST - int ziplistTest(int argc, char *argv[]); -diff --git a/tests/support/util.tcl b/tests/support/util.tcl -index 970d63314fd6..343912ac0049 100644 ---- a/tests/support/util.tcl -+++ b/tests/support/util.tcl -@@ -109,7 +109,23 @@ proc wait_done_loading r { - - # count current log lines in server's stdout - proc count_log_lines {srv_idx} { -- set _ [exec wc -l < [srv $srv_idx stdout]] -+ set _ [string trim [exec wc -l < [srv $srv_idx stdout]]] -+} -+ -+# returns the number of times a line with that pattern appears in a file -+proc count_message_lines {file pattern} { -+ set res 0 -+ # exec fails when grep exists with status other than 0 (when the patter wasn't found) -+ catch { -+ set res [string trim [exec grep $pattern $file 2> /dev/null | wc -l]] -+ } -+ return $res -+} -+ -+# returns the number of times a line with that pattern appears in the log -+proc count_log_message {srv_idx pattern} { -+ set stdout [srv $srv_idx stdout] -+ return [count_message_lines $stdout $pattern] - } - - # verify pattern exists in server's sdtout after a certain line number -diff --git a/tests/unit/violations.tcl b/tests/unit/violations.tcl -new file mode 100644 -index 000000000000..d87b9236528e ---- /dev/null -+++ b/tests/unit/violations.tcl -@@ -0,0 +1,156 @@ -+# These tests consume massive amounts of memory, and are not -+# suitable to be executed as part of the normal test suite -+set ::str500 [string repeat x 500000000] ;# 500mb -+ -+# Utility function to write big argument into redis client connection -+proc write_big_bulk {size} { -+ r write "\$$size\r\n" -+ while {$size >= 500000000} { -+ r write $::str500 -+ incr size -500000000 -+ } -+ if {$size > 0} { -+ r write [string repeat x $size] -+ } -+ r write "\r\n" -+} -+ -+# One XADD with one huge 5GB field -+# Expected to fail resulting in an empty stream -+start_server [list overrides [list save ""] ] { -+ test {XADD one huge field} { -+ r config set proto-max-bulk-len 10000000000 ;#10gb -+ r config set client-query-buffer-limit 10000000000 ;#10gb -+ r write "*5\r\n\$4\r\nXADD\r\n\$2\r\nS1\r\n\$1\r\n*\r\n" -+ r write "\$1\r\nA\r\n" -+ write_big_bulk 5000000000 ;#5gb -+ r flush -+ catch {r read} err -+ assert_match {*too large*} $err -+ r xlen S1 -+ } {0} -+} -+ -+# One XADD with one huge (exactly nearly) 4GB field -+# This uncovers the overflow in lpEncodeGetType -+# Expected to fail resulting in an empty stream -+start_server [list overrides [list save ""] ] { -+ test {XADD one huge field - 1} { -+ r config set proto-max-bulk-len 10000000000 ;#10gb -+ r config set client-query-buffer-limit 10000000000 ;#10gb -+ r write "*5\r\n\$4\r\nXADD\r\n\$2\r\nS1\r\n\$1\r\n*\r\n" -+ r write "\$1\r\nA\r\n" -+ write_big_bulk 4294967295 ;#4gb-1 -+ r flush -+ catch {r read} err -+ assert_match {*too large*} $err -+ r xlen S1 -+ } {0} -+} -+ -+# Gradually add big stream fields using repeated XADD calls -+start_server [list overrides [list save ""] ] { -+ test {several XADD big fields} { -+ r config set stream-node-max-bytes 0 -+ for {set j 0} {$j<10} {incr j} { -+ r xadd stream * 1 $::str500 2 $::str500 -+ } -+ r ping -+ r xlen stream -+ } {10} -+} -+ -+# Add over 4GB to a single stream listpack (one XADD command) -+# Expected to fail resulting in an empty stream -+start_server [list overrides [list save ""] ] { -+ test {single XADD big fields} { -+ r write "*23\r\n\$4\r\nXADD\r\n\$1\r\nS\r\n\$1\r\n*\r\n" -+ for {set j 0} {$j<10} {incr j} { -+ r write "\$1\r\n$j\r\n" -+ write_big_bulk 500000000 ;#500mb -+ } -+ r flush -+ catch {r read} err -+ assert_match {*too large*} $err -+ r xlen S -+ } {0} -+} -+ -+# Gradually add big hash fields using repeated HSET calls -+# This reproduces the overflow in the call to ziplistResize -+# Object will be converted to hashtable encoding -+start_server [list overrides [list save ""] ] { -+ r config set hash-max-ziplist-value 1000000000 ;#1gb -+ test {hash with many big fields} { -+ for {set j 0} {$j<10} {incr j} { -+ r hset h $j $::str500 -+ } -+ r object encoding h -+ } {hashtable} -+} -+ -+# Add over 4GB to a single hash field (one HSET command) -+# Object will be converted to hashtable encoding -+start_server [list overrides [list save ""] ] { -+ test {hash with one huge field} { -+ catch {r config set hash-max-ziplist-value 10000000000} ;#10gb -+ r config set proto-max-bulk-len 10000000000 ;#10gb -+ r config set client-query-buffer-limit 10000000000 ;#10gb -+ r write "*4\r\n\$4\r\nHSET\r\n\$2\r\nH1\r\n" -+ r write "\$1\r\nA\r\n" -+ write_big_bulk 5000000000 ;#5gb -+ r flush -+ r read -+ r object encoding H1 -+ } {hashtable} -+} -+ -+# Add over 4GB to a single list member (one LPUSH command) -+# Currently unsupported, and expected to fail rather than being truncated -+# Expected to fail resulting in a non-existing list -+start_server [list overrides [list save ""] ] { -+ test {list with one huge field} { -+ r config set proto-max-bulk-len 10000000000 ;#10gb -+ r config set client-query-buffer-limit 10000000000 ;#10gb -+ r write "*3\r\n\$5\r\nLPUSH\r\n\$2\r\nL1\r\n" -+ write_big_bulk 5000000000 ;#5gb -+ r flush -+ catch {r read} err -+ assert_match {*too large*} $err -+ r exists L1 -+ } {0} -+} -+ -+# SORT which attempts to store an element larger than 4GB into a list. -+# Currently unsupported and results in an assertion instead of truncation -+start_server [list overrides [list save ""] ] { -+ test {SORT adds huge field to list} { -+ r config set proto-max-bulk-len 10000000000 ;#10gb -+ r config set client-query-buffer-limit 10000000000 ;#10gb -+ r write "*3\r\n\$3\r\nSET\r\n\$2\r\nS1\r\n" -+ write_big_bulk 5000000000 ;#5gb -+ r flush -+ r read -+ assert_equal [r strlen S1] 5000000000 -+ r set S2 asdf -+ r sadd myset 1 2 -+ r mset D1 1 D2 2 -+ catch {r sort myset by D* get S* store mylist} -+ # assert_equal [count_log_message 0 "crashed by signal"] 0 - not suitable for 6.0 -+ assert_equal [count_log_message 0 "ASSERTION FAILED"] 1 -+ } -+} -+ -+# SORT which stores an integer encoded element into a list. -+# Just for coverage, no news here. -+start_server [list overrides [list save ""] ] { -+ test {SORT adds integer field to list} { -+ r set S1 asdf -+ r set S2 123 ;# integer encoded -+ assert_encoding "int" S2 -+ r sadd myset 1 2 -+ r mset D1 1 D2 2 -+ r sort myset by D* get S* store mylist -+ r llen mylist -+ } {2} -+} diff --git a/SOURCES/redis-CVE-2021-32675.patch b/SOURCES/redis-CVE-2021-32675.patch deleted file mode 100644 index 1eea12c..0000000 --- a/SOURCES/redis-CVE-2021-32675.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 5674b0057ff2903d43eaff802017eddf37c360f8 Mon Sep 17 00:00:00 2001 -From: Oran Agra -Date: Wed, 9 Jun 2021 17:31:39 +0300 -Subject: [PATCH] Prevent unauthenticated client from easily consuming lots of - memory (CVE-2021-32675) - -This change sets a low limit for multibulk and bulk length in the -protocol for unauthenticated connections, so that they can't easily -cause redis to allocate massive amounts of memory by sending just a few -characters on the network. -The new limits are 10 arguments of 16kb each (instead of 1m of 512mb) ---- - src/networking.c | 17 +++++++++++++++++ - src/server.c | 9 ++------- - src/server.h | 1 + - tests/unit/auth.tcl | 16 ++++++++++++++++ - 4 files changed, 36 insertions(+), 7 deletions(-) - -diff --git a/src/networking.c b/src/networking.c -index a61678dab2b..b02397c96f4 100644 ---- a/src/networking.c -+++ b/src/networking.c -@@ -97,6 +97,15 @@ void linkClient(client *c) { - raxInsert(server.clients_index,(unsigned char*)&id,sizeof(id),c,NULL); - } - -+int authRequired(client *c) { -+ /* Check if the user is authenticated. This check is skipped in case -+ * the default user is flagged as "nopass" and is active. */ -+ int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) || -+ (DefaultUser->flags & USER_FLAG_DISABLED)) && -+ !c->authenticated; -+ return auth_required; -+} -+ - client *createClient(connection *conn) { - client *c = zmalloc(sizeof(client)); - -@@ -1744,6 +1753,10 @@ int processMultibulkBuffer(client *c) { - addReplyError(c,"Protocol error: invalid multibulk length"); - setProtocolError("invalid mbulk count",c); - return C_ERR; -+ } else if (ll > 10 && authRequired(c)) { -+ addReplyError(c, "Protocol error: unauthenticated multibulk length"); -+ setProtocolError("unauth mbulk count", c); -+ return C_ERR; - } - - c->qb_pos = (newline-c->querybuf)+2; -@@ -1791,6 +1804,10 @@ int processMultibulkBuffer(client *c) { - addReplyError(c,"Protocol error: invalid bulk length"); - setProtocolError("invalid bulk length",c); - return C_ERR; -+ } else if (ll > 16384 && authRequired(c)) { -+ addReplyError(c, "Protocol error: unauthenticated bulk length"); -+ setProtocolError("unauth bulk length", c); -+ return C_ERR; - } - - c->qb_pos = newline-c->querybuf+2; -diff --git a/src/server.c b/src/server.c -index 93148f8e3ed..c8768b1824b 100644 ---- a/src/server.c -+++ b/src/server.c -@@ -3590,13 +3590,8 @@ int processCommand(client *c) { - int is_denyloading_command = !(c->cmd->flags & CMD_LOADING) || - (c->cmd->proc == execCommand && (c->mstate.cmd_inv_flags & CMD_LOADING)); - -- /* Check if the user is authenticated. This check is skipped in case -- * the default user is flagged as "nopass" and is active. */ -- int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) || -- (DefaultUser->flags & USER_FLAG_DISABLED)) && -- !c->authenticated; -- if (auth_required) { -- /* AUTH and HELLO and no auth modules are valid even in -+ if (authRequired(c)) { -+ /* AUTH and HELLO and no auth commands are valid even in - * non-authenticated state. */ - if (!(c->cmd->flags & CMD_NO_AUTH)) { - rejectCommand(c,shared.noautherr); -diff --git a/src/server.h b/src/server.h -index a16f2885829..530355421a8 100644 ---- a/src/server.h -+++ b/src/server.h -@@ -1743,6 +1743,7 @@ void protectClient(client *c); - void unprotectClient(client *c); - void initThreadedIO(void); - client *lookupClientByID(uint64_t id); -+int authRequired(client *c); - - #ifdef __GNUC__ - void addReplyErrorFormat(client *c, const char *fmt, ...) -diff --git a/tests/unit/auth.tcl b/tests/unit/auth.tcl -index 9080d4bf7e9..530ca7c8d91 100644 ---- a/tests/unit/auth.tcl -+++ b/tests/unit/auth.tcl -@@ -24,4 +24,20 @@ start_server {tags {"auth"} overrides {requirepass foobar}} { - r set foo 100 - r incr foo - } {101} -+ -+ test {For unauthenticated clients multibulk and bulk length are limited} { -+ set rr [redis [srv "host"] [srv "port"] 0 $::tls] -+ $rr write "*100\r\n" -+ $rr flush -+ catch {[$rr read]} e -+ assert_match {*unauthenticated multibulk length*} $e -+ $rr close -+ -+ set rr [redis [srv "host"] [srv "port"] 0 $::tls] -+ $rr write "*1\r\n\$100000000\r\n" -+ $rr flush -+ catch {[$rr read]} e -+ assert_match {*unauthenticated bulk length*} $e -+ $rr close -+ } - } diff --git a/SOURCES/redis-CVE-2021-32687.patch b/SOURCES/redis-CVE-2021-32687.patch deleted file mode 100644 index 6c5d0b4..0000000 --- a/SOURCES/redis-CVE-2021-32687.patch +++ /dev/null @@ -1,67 +0,0 @@ -From a30d367a71b7017581cf1ca104242a3c644dec0f Mon Sep 17 00:00:00 2001 -From: Oran Agra -Date: Sun, 26 Sep 2021 15:42:17 +0300 -Subject: [PATCH] Fix Integer overflow issue with intsets (CVE-2021-32687) - -The vulnerability involves changing the default set-max-intset-entries -configuration parameter to a very large value and constructing specially -crafted commands to manipulate sets ---- - src/intset.c | 4 +++- - src/rdb.c | 4 +++- - src/t_set.c | 5 ++++- - 3 files changed, 10 insertions(+), 3 deletions(-) - -diff --git a/src/intset.c b/src/intset.c -index 19f6562ed69c..3f8b754e3a3e 100644 ---- a/src/intset.c -+++ b/src/intset.c -@@ -34,6 +34,7 @@ - #include "intset.h" - #include "zmalloc.h" - #include "endianconv.h" -+#include "redisassert.h" - - /* Note that these encodings are ordered, so: - * INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */ -@@ -103,7 +104,8 @@ intset *intsetNew(void) { - - /* Resize the intset */ - static intset *intsetResize(intset *is, uint32_t len) { -- uint32_t size = len*intrev32ifbe(is->encoding); -+ uint64_t size = (uint64_t)len*intrev32ifbe(is->encoding); -+ assert(size <= SIZE_MAX - sizeof(intset)); - is = zrealloc(is,sizeof(intset)+size); - return is; - } -diff --git a/src/rdb.c b/src/rdb.c -index 82b4cf5065d1..ecd2c0e9870a 100644 ---- a/src/rdb.c -+++ b/src/rdb.c -@@ -1518,7 +1518,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { - if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; - - /* Use a regular set when there are too many entries. */ -- if (len > server.set_max_intset_entries) { -+ size_t max_entries = server.set_max_intset_entries; -+ if (max_entries >= 1<<30) max_entries = 1<<30; -+ if (len > max_entries) { - o = createSetObject(); - /* It's faster to expand the dict to the right size asap in order - * to avoid rehashing */ -diff --git a/src/t_set.c b/src/t_set.c -index f32565e41102..eea45c8df2d8 100644 ---- a/src/t_set.c -+++ b/src/t_set.c -@@ -66,7 +66,10 @@ int setTypeAdd(robj *subject, sds value) { - if (success) { - /* Convert to regular set when the intset contains - * too many entries. */ -- if (intsetLen(subject->ptr) > server.set_max_intset_entries) -+ size_t max_entries = server.set_max_intset_entries; -+ /* limit to 1G entries due to intset internals. */ -+ if (max_entries >= 1<<30) max_entries = 1<<30; -+ if (intsetLen(subject->ptr) > max_entries) - setTypeConvert(subject,OBJ_ENCODING_HT); - return 1; - } diff --git a/SOURCES/redis-CVE-2021-41099.patch b/SOURCES/redis-CVE-2021-41099.patch deleted file mode 100644 index c7ea76e..0000000 --- a/SOURCES/redis-CVE-2021-41099.patch +++ /dev/null @@ -1,91 +0,0 @@ -Backported for 6.0.9 - - -From c992857618db99776917f10bf4f2345a5fdc78b0 Mon Sep 17 00:00:00 2001 -From: Yossi Gottlieb -Date: Mon, 22 Feb 2021 15:41:32 +0200 -Subject: [PATCH] Fix integer overflow (CVE-2021-21309). (#8522) - -On 32-bit systems, setting the proto-max-bulk-len config parameter to a high value may result with integer overflow and a subsequent heap overflow when parsing an input bulk (CVE-2021-21309). - -This fix has two parts: - -Set a reasonable limit to the config parameter. -Add additional checks to prevent the problem in other potential but unknown code paths. - -(cherry picked from commit d32f2e9999ce003bad0bd2c3bca29f64dcce4433) ---- - src/config.c | 2 +- - src/sds.c | 3 +++ - src/zmalloc.c | 10 ++++++++++ - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/src/sds.c b/src/sds.c -index dc664ca9bc4f..4dbb41d2b703 100644 ---- a/src/sds.c -+++ b/src/sds.c -@@ -96,6 +96,7 @@ sds sdsnewlen(const void *init, size_t initlen) { - int hdrlen = sdsHdrSize(type); - unsigned char *fp; /* flags pointer. */ - -+ assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */ - sh = s_malloc(hdrlen+initlen+1); - if (sh == NULL) return NULL; - if (init==SDS_NOINIT) -@@ -214,6 +215,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); - newlen = (len+addlen); -+ assert(newlen > len); /* Catch size_t overflow */ - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; - else -@@ -227,6 +229,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); -+ assert(hdrlen + newlen + 1 > len); /* Catch size_t overflow */ - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; - -From c6ad876774f3cc11e32681ea02a2eead00f2c521 Mon Sep 17 00:00:00 2001 -From: YiyuanGUO -Date: Wed, 29 Sep 2021 10:20:35 +0300 -Subject: [PATCH] Fix integer overflow in _sdsMakeRoomFor (CVE-2021-41099) - ---- - src/sds.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/sds.c b/src/sds.c -index 4dbb41d2b703..a43a1a5cbeed 100644 ---- a/src/sds.c -+++ b/src/sds.c -@@ -205,7 +205,7 @@ void sdsclear(sds s) { - sds sdsMakeRoomFor(sds s, size_t addlen) { - void *sh, *newsh; - size_t avail = sdsavail(s); -- size_t len, newlen; -+ size_t len, newlen, reqlen; - char type, oldtype = s[-1] & SDS_TYPE_MASK; - int hdrlen; - -@@ -214,7 +214,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { - - len = sdslen(s); - sh = (char*)s-sdsHdrSize(oldtype); -- newlen = (len+addlen); -+ reqlen = newlen = (len+addlen); - assert(newlen > len); /* Catch size_t overflow */ - if (newlen < SDS_MAX_PREALLOC) - newlen *= 2; -@@ -229,7 +229,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { - if (type == SDS_TYPE_5) type = SDS_TYPE_8; - - hdrlen = sdsHdrSize(type); -- assert(hdrlen + newlen + 1 > len); /* Catch size_t overflow */ -+ assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */ - if (oldtype==type) { - newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) return NULL; diff --git a/SOURCES/redis-config.patch b/SOURCES/redis-config.patch index aad26a7..77cb355 100644 --- a/SOURCES/redis-config.patch +++ b/SOURCES/redis-config.patch @@ -7,9 +7,10 @@ Revert: 90555566ed5cbd3e1c3df1293ba3bbf6098e34c3 See discussion about this breaking change in https://github.com/redis/redis/issues/8051 ---- redis-6.0.9/src/config.c 2020-10-27 08:12:01.000000000 +0100 -+++ redis-6.0.8/src/config.c 2020-09-10 13:09:00.000000000 +0200 -@@ -1568,62 +1568,60 @@ +diff -up ./src/config.c.rev ./src/config.c +--- ./src/config.c.rev 2022-05-09 14:48:31.118296748 +0200 ++++ ./src/config.c 2022-05-09 14:48:41.571163767 +0200 +@@ -1605,62 +1605,60 @@ void rewriteConfigRemoveOrphaned(struct dictReleaseIterator(di); } @@ -41,39 +42,6 @@ https://github.com/redis/redis/issues/8051 - serverLog(LL_WARNING, "Config file full path is too long"); - errno = ENAMETOOLONG; - return retval; -- } -- --#ifdef _GNU_SOURCE -- fd = mkostemp(tmp_conffile, O_CLOEXEC); --#else -- /* There's a theoretical chance here to leak the FD if a module thread forks & execv in the middle */ -- fd = mkstemp(tmp_conffile); --#endif -- -- if (fd == -1) { -- serverLog(LL_WARNING, "Could not create tmp config file (%s)", strerror(errno)); -- return retval; -- } -- -- while (offset < sdslen(content)) { -- written_bytes = write(fd, content + offset, sdslen(content) - offset); -- if (written_bytes <= 0) { -- if (errno == EINTR) continue; /* FD is blocking, no other retryable errors */ -- serverLog(LL_WARNING, "Failed after writing (%zd) bytes to tmp config file (%s)", offset, strerror(errno)); -- goto cleanup; -- } -- offset+=written_bytes; -- } -- -- if (fsync(fd)) -- serverLog(LL_WARNING, "Could not sync tmp config file to disk (%s)", strerror(errno)); -- else if (fchmod(fd, 0644) == -1) -- serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno)); -- else if (rename(tmp_conffile, configfile) == -1) -- serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno)); -- else { -- retval = 0; -- serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile); + int retval = 0; + int fd = open(configfile,O_RDWR|O_CREAT,0644); + int content_size = sdslen(content), padding = 0; @@ -86,8 +54,18 @@ https://github.com/redis/redis/issues/8051 + if (fstat(fd,&sb) == -1) { + close(fd); + return -1; /* errno set by fstat(). */ -+ } -+ + } + +-#ifdef _GNU_SOURCE +- fd = mkostemp(tmp_conffile, O_CLOEXEC); +-#else +- /* There's a theoretical chance here to leak the FD if a module thread forks & execv in the middle */ +- fd = mkstemp(tmp_conffile); +-#endif +- +- if (fd == -1) { +- serverLog(LL_WARNING, "Could not create tmp config file (%s)", strerror(errno)); +- return retval; + /* 2) Pad the content at least match the old file size. */ + content_padded = sdsdup(content); + if (content_size < sb.st_size) { @@ -97,20 +75,38 @@ https://github.com/redis/redis/issues/8051 + content_padded = sdsgrowzero(content_padded,sb.st_size); + content_padded[content_size] = '\n'; + memset(content_padded+content_size+1,'#',padding-1); -+ } -+ + } + +- while (offset < sdslen(content)) { +- written_bytes = write(fd, content + offset, sdslen(content) - offset); +- if (written_bytes <= 0) { +- if (errno == EINTR) continue; /* FD is blocking, no other retryable errors */ +- serverLog(LL_WARNING, "Failed after writing (%zd) bytes to tmp config file (%s)", offset, strerror(errno)); +- goto cleanup; +- } +- offset+=written_bytes; + /* 3) Write the new content using a single write(2). */ + if (write(fd,content_padded,strlen(content_padded)) == -1) { + retval = -1; + goto cleanup; -+ } -+ + } + +- if (fsync(fd)) +- serverLog(LL_WARNING, "Could not sync tmp config file to disk (%s)", strerror(errno)); +- else if (fchmod(fd, 0644 & ~server.umask) == -1) +- serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno)); +- else if (rename(tmp_conffile, configfile) == -1) +- serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno)); +- else { +- retval = 0; +- serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile); +- } + /* 4) Truncate the file to the right length if we used padding. */ + if (padding) { + if (ftruncate(fd,content_size) == -1) { + /* Non critical error... */ + } - } ++ } cleanup: + sdsfree(content_padded); @@ -119,4 +115,3 @@ https://github.com/redis/redis/issues/8051 return retval; } - diff --git a/SPECS/redis.spec b/SPECS/redis.spec index 503f1b7..e251598 100644 --- a/SPECS/redis.spec +++ b/SPECS/redis.spec @@ -19,8 +19,8 @@ %global macrosdir %(d=%{_rpmconfigdir}/macros.d; [ -d $d ] || d=%{_sysconfdir}/rpm; echo $d) Name: redis -Version: 6.0.9 -Release: 5%{?dist} +Version: 6.2.7 +Release: 1%{?dist} Summary: A persistent key-value database # redis, jemalloc, linenoise, lzf, hiredis are BSD # lua is MIT @@ -46,18 +46,10 @@ Source10: https://github.com/antirez/%{name}-doc/archive/%{doc_commit}/ # Update configuration for Fedora # https://github.com/antirez/redis/pull/3491 - man pages Patch0001: 0001-1st-man-pageis-for-redis-cli-redis-benchmark-redis-c.patch -# https://github.com/antirez/redis/pull/3494 - symlink -Patch0002: 0002-install-redis-check-rdb-as-a-symlink-instead-of-dupl.patch # revert BC break Patch0003: redis-config.patch # Security patches -Patch100: redis-CVE-2021-26477.patch -Patch101: redis-CVE-2021-32687.patch -Patch102: redis-CVE-2021-32626.patch -Patch103: redis-CVE-2021-32627.patch -Patch104: redis-CVE-2021-41099.patch -Patch105: redis-CVE-2021-32675.patch BuildRequires: gcc %if %{with tests} @@ -75,7 +67,7 @@ Requires(post): systemd Requires(preun): systemd Requires(postun): systemd # from deps/hiredis/hiredis.h -Provides: bundled(hiredis) = 0.14.0 +Provides: bundled(hiredis) = 1.0.0 # from deps/jemalloc/VERSION Provides: bundled(jemalloc) = 5.1.0 # from deps/lua/src/lua.h @@ -140,14 +132,7 @@ administration and development. %setup -q mv ../%{name}-doc-%{doc_commit} doc %patch0001 -p1 -%patch0002 -p1 -%patch0003 -p1 -%patch100 -p1 -b .cve-2011-29477 -%patch101 -p1 -b .cve-2011-32687 -%patch102 -p1 -b .cve-2011-32626 -%patch103 -p1 -b .cve-2011-32627 -%patch104 -p1 -b .cve-2011-41099 -%patch105 -p1 -b .cve-2011-32675 +%patch0003 -p1 -b .rev mv deps/lua/COPYRIGHT COPYRIGHT-lua mv deps/jemalloc/COPYING COPYING-jemalloc @@ -296,6 +281,9 @@ exit 0 %changelog +* Mon May 9 2022 Remi Collet - 6.2.7-1 +- rebase to 6.2.7 #1999873 + * Mon Oct 11 2021 Remi Collet - 6.0.9-5 - fix denial of service via Redis Standard Protocol (RESP) request CVE-2021-32675