diff options
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/0001-src-Do-not-reset-FINAL_LIBS.patch | 10 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32626.patch | 148 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32627-CVE-2021-32628.patch | 873 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32675.patch | 129 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32687.patch | 67 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch | 257 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-32762.patch | 68 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/CVE-2021-41099.patch | 47 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29477.patch | 35 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29478.patch | 42 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis/fix-CVE-2021-32625.patch | 61 | ||||
| -rw-r--r-- | meta-oe/recipes-extended/redis/redis_6.2.6.bb (renamed from meta-oe/recipes-extended/redis/redis_6.2.2.bb) | 15 |
12 files changed, 6 insertions, 1746 deletions
diff --git a/meta-oe/recipes-extended/redis/redis/0001-src-Do-not-reset-FINAL_LIBS.patch b/meta-oe/recipes-extended/redis/redis/0001-src-Do-not-reset-FINAL_LIBS.patch index b5c4133e31..43d86094d5 100644 --- a/meta-oe/recipes-extended/redis/redis/0001-src-Do-not-reset-FINAL_LIBS.patch +++ b/meta-oe/recipes-extended/redis/redis/0001-src-Do-not-reset-FINAL_LIBS.patch | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | From 97584e1eb78dc18599534b47b6670c20c63f5ee2 Mon Sep 17 00:00:00 2001 | 1 | From aff8b278bd36085036d302027bc215483ad7f32b Mon Sep 17 00:00:00 2001 |
| 2 | From: Khem Raj <raj.khem@gmail.com> | 2 | From: Khem Raj <raj.khem@gmail.com> |
| 3 | Date: Tue, 10 Sep 2019 20:04:26 -0700 | 3 | Date: Tue, 10 Sep 2019 20:04:26 -0700 |
| 4 | Subject: [PATCH] src: Do not reset FINAL_LIBS | 4 | Subject: [PATCH] src: Do not reset FINAL_LIBS |
| @@ -15,10 +15,10 @@ Signed-off-by: Khem Raj <raj.khem@gmail.com> | |||
| 15 | 1 file changed, 1 insertion(+), 1 deletion(-) | 15 | 1 file changed, 1 insertion(+), 1 deletion(-) |
| 16 | 16 | ||
| 17 | diff --git a/src/Makefile b/src/Makefile | 17 | diff --git a/src/Makefile b/src/Makefile |
| 18 | index 7f7c625..c71dd3b 100644 | 18 | index 7a7168c..d0680e8 100644 |
| 19 | --- a/src/Makefile | 19 | --- a/src/Makefile |
| 20 | +++ b/src/Makefile | 20 | +++ b/src/Makefile |
| 21 | @@ -75,7 +75,7 @@ endif | 21 | @@ -91,7 +91,7 @@ endif |
| 22 | 22 | ||
| 23 | FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) | 23 | FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) |
| 24 | FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG) | 24 | FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG) |
| @@ -26,7 +26,7 @@ index 7f7c625..c71dd3b 100644 | |||
| 26 | +FINAL_LIBS+=-lm | 26 | +FINAL_LIBS+=-lm |
| 27 | DEBUG=-g -ggdb | 27 | DEBUG=-g -ggdb |
| 28 | 28 | ||
| 29 | # Linux ARM needs -latomic at linking time | 29 | # Linux ARM32 needs -latomic at linking time |
| 30 | -- | 30 | -- |
| 31 | 2.23.0 | 31 | 2.17.1 |
| 32 | 32 | ||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32626.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32626.patch deleted file mode 100644 index 0cfc12b3d9..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32626.patch +++ /dev/null | |||
| @@ -1,148 +0,0 @@ | |||
| 1 | From 6ce827254484fd850240549c98c74bca77980cc0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: "meir@redislabs.com" <meir@redislabs.com> | ||
| 3 | Date: Sun, 13 Jun 2021 14:27:18 +0300 | ||
| 4 | Subject: [PATCH] Fix invalid memory write on lua stack overflow | ||
| 5 | {CVE-2021-32626} | ||
| 6 | MIME-Version: 1.0 | ||
| 7 | Content-Type: text/plain; charset=UTF-8 | ||
| 8 | Content-Transfer-Encoding: 8bit | ||
| 9 | |||
| 10 | When LUA call our C code, by default, the LUA stack has room for 20 | ||
| 11 | elements. In most cases, this is more than enough but sometimes it's not | ||
| 12 | and the caller must verify the LUA stack size before he pushes elements. | ||
| 13 | |||
| 14 | On 3 places in the code, there was no verification of the LUA stack size. | ||
| 15 | On specific inputs this missing verification could have lead to invalid | ||
| 16 | memory write: | ||
| 17 | 1. On 'luaReplyToRedisReply', one might return a nested reply that will | ||
| 18 | explode the LUA stack. | ||
| 19 | 2. On 'redisProtocolToLuaType', the Redis reply might be deep enough | ||
| 20 | to explode the LUA stack (notice that currently there is no such | ||
| 21 | command in Redis that returns such a nested reply, but modules might | ||
| 22 | do it) | ||
| 23 | 3. On 'ldbRedis', one might give a command with enough arguments to | ||
| 24 | explode the LUA stack (all the arguments will be pushed to the LUA | ||
| 25 | stack) | ||
| 26 | |||
| 27 | This commit is solving all those 3 issues by calling 'lua_checkstack' and | ||
| 28 | verify that there is enough room in the LUA stack to push elements. In | ||
| 29 | case 'lua_checkstack' returns an error (there is not enough room in the | ||
| 30 | LUA stack and it's not possible to increase the stack), we will do the | ||
| 31 | following: | ||
| 32 | 1. On 'luaReplyToRedisReply', we will return an error to the user. | ||
| 33 | 2. On 'redisProtocolToLuaType' we will exit with panic (we assume this | ||
| 34 | scenario is rare because it can only happen with a module). | ||
| 35 | 3. On 'ldbRedis', we return an error. | ||
| 36 | |||
| 37 | CVE: CVE-2021-32626 | ||
| 38 | Upstream-Status: Backport[https://github.com/redis/redis/commit/666ed7facf4524bf6d19b11b20faa2cf93fdf591] | ||
| 39 | |||
| 40 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 41 | --- | ||
| 42 | src/scripting.c | 41 +++++++++++++++++++++++++++++++++++++++++ | ||
| 43 | 1 file changed, 41 insertions(+) | ||
| 44 | |||
| 45 | diff --git a/src/scripting.c b/src/scripting.c | ||
| 46 | index 299e608..81c88fb 100644 | ||
| 47 | --- a/src/scripting.c | ||
| 48 | +++ b/src/scripting.c | ||
| 49 | @@ -128,6 +128,16 @@ void sha1hex(char *digest, char *script, size_t len) { | ||
| 50 | */ | ||
| 51 | |||
| 52 | char *redisProtocolToLuaType(lua_State *lua, char* reply) { | ||
| 53 | + | ||
| 54 | + if (!lua_checkstack(lua, 5)) { | ||
| 55 | + /* | ||
| 56 | + * Increase the Lua stack if needed, to make sure there is enough room | ||
| 57 | + * to push 5 elements to the stack. On failure, exit with panic. | ||
| 58 | + * Notice that we need, in the worst case, 5 elements because redisProtocolToLuaType_Aggregate | ||
| 59 | + * might push 5 elements to the Lua stack.*/ | ||
| 60 | + serverPanic("lua stack limit reach when parsing redis.call reply"); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | char *p = reply; | ||
| 64 | |||
| 65 | switch(*p) { | ||
| 66 | @@ -220,6 +230,11 @@ char *redisProtocolToLuaType_Aggregate(lua_State *lua, char *reply, int atype) { | ||
| 67 | if (atype == '%') { | ||
| 68 | p = redisProtocolToLuaType(lua,p); | ||
| 69 | } else { | ||
| 70 | + if (!lua_checkstack(lua, 1)) { | ||
| 71 | + /* Notice that here we need to check the stack again because the recursive | ||
| 72 | + * call to redisProtocolToLuaType might have use the room allocated in the stack */ | ||
| 73 | + serverPanic("lua stack limit reach when parsing redis.call reply"); | ||
| 74 | + } | ||
| 75 | lua_pushboolean(lua,1); | ||
| 76 | } | ||
| 77 | lua_settable(lua,-3); | ||
| 78 | @@ -339,6 +354,17 @@ void luaSortArray(lua_State *lua) { | ||
| 79 | /* Reply to client 'c' converting the top element in the Lua stack to a | ||
| 80 | * Redis reply. As a side effect the element is consumed from the stack. */ | ||
| 81 | void luaReplyToRedisReply(client *c, lua_State *lua) { | ||
| 82 | + | ||
| 83 | + if (!lua_checkstack(lua, 4)) { | ||
| 84 | + /* Increase the Lua stack if needed to make sure there is enough room | ||
| 85 | + * to push 4 elements to the stack. On failure, return error. | ||
| 86 | + * Notice that we need, in the worst case, 4 elements because returning a map might | ||
| 87 | + * require push 4 elements to the Lua stack.*/ | ||
| 88 | + addReplyErrorFormat(c, "reached lua stack limit"); | ||
| 89 | + lua_pop(lua,1); // pop the element from the stack | ||
| 90 | + return; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | int t = lua_type(lua,-1); | ||
| 94 | |||
| 95 | switch(t) { | ||
| 96 | @@ -362,6 +388,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { | ||
| 97 | * field. */ | ||
| 98 | |||
| 99 | /* Handle error reply. */ | ||
| 100 | + // we took care of the stack size on function start | ||
| 101 | lua_pushstring(lua,"err"); | ||
| 102 | lua_gettable(lua,-2); | ||
| 103 | t = lua_type(lua,-1); | ||
| 104 | @@ -404,6 +431,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { | ||
| 105 | if (t == LUA_TTABLE) { | ||
| 106 | int maplen = 0; | ||
| 107 | void *replylen = addReplyDeferredLen(c); | ||
| 108 | + /* we took care of the stack size on function start */ | ||
| 109 | lua_pushnil(lua); /* Use nil to start iteration. */ | ||
| 110 | while (lua_next(lua,-2)) { | ||
| 111 | /* Stack now: table, key, value */ | ||
| 112 | @@ -426,6 +454,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { | ||
| 113 | if (t == LUA_TTABLE) { | ||
| 114 | int setlen = 0; | ||
| 115 | void *replylen = addReplyDeferredLen(c); | ||
| 116 | + /* we took care of the stack size on function start */ | ||
| 117 | lua_pushnil(lua); /* Use nil to start iteration. */ | ||
| 118 | while (lua_next(lua,-2)) { | ||
| 119 | /* Stack now: table, key, true */ | ||
| 120 | @@ -445,6 +474,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { | ||
| 121 | void *replylen = addReplyDeferredLen(c); | ||
| 122 | int j = 1, mbulklen = 0; | ||
| 123 | while(1) { | ||
| 124 | + /* we took care of the stack size on function start */ | ||
| 125 | lua_pushnumber(lua,j++); | ||
| 126 | lua_gettable(lua,-2); | ||
| 127 | t = lua_type(lua,-1); | ||
| 128 | @@ -2546,6 +2576,17 @@ void ldbEval(lua_State *lua, sds *argv, int argc) { | ||
| 129 | void ldbRedis(lua_State *lua, sds *argv, int argc) { | ||
| 130 | int j, saved_rc = server.lua_replicate_commands; | ||
| 131 | |||
| 132 | + if (!lua_checkstack(lua, argc + 1)) { | ||
| 133 | + /* Increase the Lua stack if needed to make sure there is enough room | ||
| 134 | + * to push 'argc + 1' elements to the stack. On failure, return error. | ||
| 135 | + * Notice that we need, in worst case, 'argc + 1' elements because we push all the arguments | ||
| 136 | + * given by the user (without the first argument) and we also push the 'redis' global table and | ||
| 137 | + * 'redis.call' function so: | ||
| 138 | + * (1 (redis table)) + (1 (redis.call function)) + (argc - 1 (all arguments without the first)) = argc + 1*/ | ||
| 139 | + ldbLogRedisReply("max lua stack reached"); | ||
| 140 | + return; | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | lua_getglobal(lua,"redis"); | ||
| 144 | lua_pushstring(lua,"call"); | ||
| 145 | lua_gettable(lua,-2); /* Stack: redis, redis.call */ | ||
| 146 | -- | ||
| 147 | 2.17.1 | ||
| 148 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32627-CVE-2021-32628.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32627-CVE-2021-32628.patch deleted file mode 100644 index 3c60a3e678..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32627-CVE-2021-32628.patch +++ /dev/null | |||
| @@ -1,873 +0,0 @@ | |||
| 1 | From 2775a3526e3e8bb040e72995231632c801977395 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Thu, 3 Jun 2021 12:10:02 +0300 | ||
| 4 | Subject: [PATCH] Fix ziplist and listpack overflows and truncations | ||
| 5 | (CVE-2021-32627, CVE-2021-32628) | ||
| 6 | |||
| 7 | - fix possible heap corruption in ziplist and listpack resulting by trying to | ||
| 8 | allocate more than the maximum size of 4GB. | ||
| 9 | - prevent ziplist (hash and zset) from reaching size of above 1GB, will be | ||
| 10 | converted to HT encoding, that's not a useful size. | ||
| 11 | - prevent listpack (stream) from reaching size of above 1GB. | ||
| 12 | - XADD will start a new listpack if the new record may cause the previous | ||
| 13 | listpack to grow over 1GB. | ||
| 14 | - XADD will respond with an error if a single stream record is over 1GB | ||
| 15 | - List type (ziplist in quicklist) was truncating strings that were over 4GB, | ||
| 16 | now it'll respond with an error. | ||
| 17 | |||
| 18 | CVE: CVE-2021-32627,CVE-2021-32628 | ||
| 19 | Upstream-Status: Backport[https://github.com/redis/redis/commit/f6a40570fa63d5afdd596c78083d754081d80ae3] | ||
| 20 | |||
| 21 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 22 | |||
| 23 | --- | ||
| 24 | src/geo.c | 5 +- | ||
| 25 | src/listpack.c | 2 +- | ||
| 26 | src/module.c | 6 +- | ||
| 27 | src/quicklist.c | 16 +++- | ||
| 28 | src/rdb.c | 45 +++++++---- | ||
| 29 | src/server.h | 2 +- | ||
| 30 | src/t_hash.c | 13 +++- | ||
| 31 | src/t_list.c | 29 +++++++ | ||
| 32 | src/t_stream.c | 48 +++++++++--- | ||
| 33 | src/t_zset.c | 62 +++++++++------ | ||
| 34 | src/ziplist.c | 17 ++++- | ||
| 35 | src/ziplist.h | 1 + | ||
| 36 | tests/unit/violations.tcl | 156 ++++++++++++++++++++++++++++++++++++++ | ||
| 37 | 13 files changed, 341 insertions(+), 61 deletions(-) | ||
| 38 | create mode 100644 tests/unit/violations.tcl | ||
| 39 | |||
| 40 | diff --git a/src/geo.c b/src/geo.c | ||
| 41 | index 7c75738a2..893f78a7e 100644 | ||
| 42 | --- a/src/geo.c | ||
| 43 | +++ b/src/geo.c | ||
| 44 | @@ -770,7 +770,7 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { | ||
| 45 | robj *zobj; | ||
| 46 | zset *zs; | ||
| 47 | int i; | ||
| 48 | - size_t maxelelen = 0; | ||
| 49 | + size_t maxelelen = 0, totelelen = 0; | ||
| 50 | |||
| 51 | if (returned_items) { | ||
| 52 | zobj = createZsetObject(); | ||
| 53 | @@ -785,13 +785,14 @@ void georadiusGeneric(client *c, int srcKeyIndex, int flags) { | ||
| 54 | size_t elelen = sdslen(gp->member); | ||
| 55 | |||
| 56 | if (maxelelen < elelen) maxelelen = elelen; | ||
| 57 | + totelelen += elelen; | ||
| 58 | znode = zslInsert(zs->zsl,score,gp->member); | ||
| 59 | serverAssert(dictAdd(zs->dict,gp->member,&znode->score) == DICT_OK); | ||
| 60 | gp->member = NULL; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (returned_items) { | ||
| 64 | - zsetConvertToZiplistIfNeeded(zobj,maxelelen); | ||
| 65 | + zsetConvertToZiplistIfNeeded(zobj,maxelelen,totelelen); | ||
| 66 | setKey(c,c->db,storekey,zobj); | ||
| 67 | decrRefCount(zobj); | ||
| 68 | notifyKeyspaceEvent(NOTIFY_ZSET,flags & GEOSEARCH ? "geosearchstore" : "georadiusstore",storekey, | ||
| 69 | diff --git a/src/listpack.c b/src/listpack.c | ||
| 70 | index ee256bad3..27622d4a5 100644 | ||
| 71 | --- a/src/listpack.c | ||
| 72 | +++ b/src/listpack.c | ||
| 73 | @@ -313,7 +313,7 @@ int lpEncodeGetType(unsigned char *ele, uint32_t size, unsigned char *intenc, ui | ||
| 74 | } else { | ||
| 75 | if (size < 64) *enclen = 1+size; | ||
| 76 | else if (size < 4096) *enclen = 2+size; | ||
| 77 | - else *enclen = 5+size; | ||
| 78 | + else *enclen = 5+(uint64_t)size; | ||
| 79 | return LP_ENCODING_STRING; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | diff --git a/src/module.c b/src/module.c | ||
| 83 | index bf6580a60..adca9dc9c 100644 | ||
| 84 | --- a/src/module.c | ||
| 85 | +++ b/src/module.c | ||
| 86 | @@ -3319,6 +3319,7 @@ int RM_HashGet(RedisModuleKey *key, int flags, ...) { | ||
| 87 | * - EDOM if the given ID was 0-0 or not greater than all other IDs in the | ||
| 88 | * stream (only if the AUTOID flag is unset) | ||
| 89 | * - EFBIG if the stream has reached the last possible ID | ||
| 90 | + * - ERANGE if the elements are too large to be stored. | ||
| 91 | */ | ||
| 92 | int RM_StreamAdd(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisModuleString **argv, long numfields) { | ||
| 93 | /* Validate args */ | ||
| 94 | @@ -3362,8 +3363,9 @@ int RM_StreamAdd(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisM | ||
| 95 | use_id_ptr = &use_id; | ||
| 96 | } | ||
| 97 | if (streamAppendItem(s, argv, numfields, &added_id, use_id_ptr) == C_ERR) { | ||
| 98 | - /* ID not greater than all existing IDs in the stream */ | ||
| 99 | - errno = EDOM; | ||
| 100 | + /* Either the ID not greater than all existing IDs in the stream, or | ||
| 101 | + * the elements are too large to be stored. either way, errno is already | ||
| 102 | + * set by streamAppendItem. */ | ||
| 103 | return REDISMODULE_ERR; | ||
| 104 | } | ||
| 105 | /* Postponed signalKeyAsReady(). Done implicitly by moduleCreateEmptyKey() | ||
| 106 | diff --git a/src/quicklist.c b/src/quicklist.c | ||
| 107 | index 5a1e41dcc..a9f8b43b1 100644 | ||
| 108 | --- a/src/quicklist.c | ||
| 109 | +++ b/src/quicklist.c | ||
| 110 | @@ -45,11 +45,16 @@ | ||
| 111 | #define REDIS_STATIC static | ||
| 112 | #endif | ||
| 113 | |||
| 114 | -/* Optimization levels for size-based filling */ | ||
| 115 | +/* Optimization levels for size-based filling. | ||
| 116 | + * Note that the largest possible limit is 16k, so even if each record takes | ||
| 117 | + * just one byte, it still won't overflow the 16 bit count field. */ | ||
| 118 | static const size_t optimization_level[] = {4096, 8192, 16384, 32768, 65536}; | ||
| 119 | |||
| 120 | /* Maximum size in bytes of any multi-element ziplist. | ||
| 121 | - * Larger values will live in their own isolated ziplists. */ | ||
| 122 | + * Larger values will live in their own isolated ziplists. | ||
| 123 | + * This is used only if we're limited by record count. when we're limited by | ||
| 124 | + * size, the maximum limit is bigger, but still safe. | ||
| 125 | + * 8k is a recommended / default size limit */ | ||
| 126 | #define SIZE_SAFETY_LIMIT 8192 | ||
| 127 | |||
| 128 | /* Minimum ziplist size in bytes for attempting compression. */ | ||
| 129 | @@ -444,6 +449,8 @@ REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node, | ||
| 130 | unsigned int new_sz = node->sz + sz + ziplist_overhead; | ||
| 131 | if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill))) | ||
| 132 | return 1; | ||
| 133 | + /* when we return 1 above we know that the limit is a size limit (which is | ||
| 134 | + * safe, see comments next to optimization_level and SIZE_SAFETY_LIMIT) */ | ||
| 135 | else if (!sizeMeetsSafetyLimit(new_sz)) | ||
| 136 | return 0; | ||
| 137 | else if ((int)node->count < fill) | ||
| 138 | @@ -463,6 +470,8 @@ REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a, | ||
| 139 | unsigned int merge_sz = a->sz + b->sz - 11; | ||
| 140 | if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(merge_sz, fill))) | ||
| 141 | return 1; | ||
| 142 | + /* when we return 1 above we know that the limit is a size limit (which is | ||
| 143 | + * safe, see comments next to optimization_level and SIZE_SAFETY_LIMIT) */ | ||
| 144 | else if (!sizeMeetsSafetyLimit(merge_sz)) | ||
| 145 | return 0; | ||
| 146 | else if ((int)(a->count + b->count) <= fill) | ||
| 147 | @@ -482,6 +491,7 @@ REDIS_STATIC int _quicklistNodeAllowMerge(const quicklistNode *a, | ||
| 148 | * Returns 1 if new head created. */ | ||
| 149 | int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) { | ||
| 150 | quicklistNode *orig_head = quicklist->head; | ||
| 151 | + assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ | ||
| 152 | if (likely( | ||
| 153 | _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) { | ||
| 154 | quicklist->head->zl = | ||
| 155 | @@ -505,6 +515,7 @@ int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) { | ||
| 156 | * Returns 1 if new tail created. */ | ||
| 157 | int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) { | ||
| 158 | quicklistNode *orig_tail = quicklist->tail; | ||
| 159 | + assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ | ||
| 160 | if (likely( | ||
| 161 | _quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) { | ||
| 162 | quicklist->tail->zl = | ||
| 163 | @@ -847,6 +858,7 @@ REDIS_STATIC void _quicklistInsert(quicklist *quicklist, quicklistEntry *entry, | ||
| 164 | int fill = quicklist->fill; | ||
| 165 | quicklistNode *node = entry->node; | ||
| 166 | quicklistNode *new_node = NULL; | ||
| 167 | + assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */ | ||
| 168 | |||
| 169 | if (!node) { | ||
| 170 | /* we have no reference node, so let's create only node in the list */ | ||
| 171 | diff --git a/src/rdb.c b/src/rdb.c | ||
| 172 | index 53f67a72e..5456c1d80 100644 | ||
| 173 | --- a/src/rdb.c | ||
| 174 | +++ b/src/rdb.c | ||
| 175 | @@ -1625,7 +1625,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 176 | } else if (rdbtype == RDB_TYPE_ZSET_2 || rdbtype == RDB_TYPE_ZSET) { | ||
| 177 | /* Read list/set value. */ | ||
| 178 | uint64_t zsetlen; | ||
| 179 | - size_t maxelelen = 0; | ||
| 180 | + size_t maxelelen = 0, totelelen = 0; | ||
| 181 | zset *zs; | ||
| 182 | |||
| 183 | if ((zsetlen = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; | ||
| 184 | @@ -1665,6 +1665,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 185 | |||
| 186 | /* Don't care about integer-encoded strings. */ | ||
| 187 | if (sdslen(sdsele) > maxelelen) maxelelen = sdslen(sdsele); | ||
| 188 | + totelelen += sdslen(sdsele); | ||
| 189 | |||
| 190 | znode = zslInsert(zs->zsl,score,sdsele); | ||
| 191 | if (dictAdd(zs->dict,sdsele,&znode->score) != DICT_OK) { | ||
| 192 | @@ -1677,8 +1678,11 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 193 | |||
| 194 | /* Convert *after* loading, since sorted sets are not stored ordered. */ | ||
| 195 | if (zsetLength(o) <= server.zset_max_ziplist_entries && | ||
| 196 | - maxelelen <= server.zset_max_ziplist_value) | ||
| 197 | - zsetConvert(o,OBJ_ENCODING_ZIPLIST); | ||
| 198 | + maxelelen <= server.zset_max_ziplist_value && | ||
| 199 | + ziplistSafeToAdd(NULL, totelelen)) | ||
| 200 | + { | ||
| 201 | + zsetConvert(o,OBJ_ENCODING_ZIPLIST); | ||
| 202 | + } | ||
| 203 | } else if (rdbtype == RDB_TYPE_HASH) { | ||
| 204 | uint64_t len; | ||
| 205 | int ret; | ||
| 206 | @@ -1731,21 +1735,30 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | - /* Add pair to ziplist */ | ||
| 211 | - o->ptr = ziplistPush(o->ptr, (unsigned char*)field, | ||
| 212 | - sdslen(field), ZIPLIST_TAIL); | ||
| 213 | - o->ptr = ziplistPush(o->ptr, (unsigned char*)value, | ||
| 214 | - sdslen(value), ZIPLIST_TAIL); | ||
| 215 | - | ||
| 216 | /* Convert to hash table if size threshold is exceeded */ | ||
| 217 | if (sdslen(field) > server.hash_max_ziplist_value || | ||
| 218 | - sdslen(value) > server.hash_max_ziplist_value) | ||
| 219 | + sdslen(value) > server.hash_max_ziplist_value || | ||
| 220 | + !ziplistSafeToAdd(o->ptr, sdslen(field)+sdslen(value))) | ||
| 221 | { | ||
| 222 | - sdsfree(field); | ||
| 223 | - sdsfree(value); | ||
| 224 | hashTypeConvert(o, OBJ_ENCODING_HT); | ||
| 225 | + ret = dictAdd((dict*)o->ptr, field, value); | ||
| 226 | + if (ret == DICT_ERR) { | ||
| 227 | + rdbReportCorruptRDB("Duplicate hash fields detected"); | ||
| 228 | + if (dupSearchDict) dictRelease(dupSearchDict); | ||
| 229 | + sdsfree(value); | ||
| 230 | + sdsfree(field); | ||
| 231 | + decrRefCount(o); | ||
| 232 | + return NULL; | ||
| 233 | + } | ||
| 234 | break; | ||
| 235 | } | ||
| 236 | + | ||
| 237 | + /* Add pair to ziplist */ | ||
| 238 | + o->ptr = ziplistPush(o->ptr, (unsigned char*)field, | ||
| 239 | + sdslen(field), ZIPLIST_TAIL); | ||
| 240 | + o->ptr = ziplistPush(o->ptr, (unsigned char*)value, | ||
| 241 | + sdslen(value), ZIPLIST_TAIL); | ||
| 242 | + | ||
| 243 | sdsfree(field); | ||
| 244 | sdsfree(value); | ||
| 245 | } | ||
| 246 | @@ -1858,12 +1871,11 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 247 | while ((zi = zipmapNext(zi, &fstr, &flen, &vstr, &vlen)) != NULL) { | ||
| 248 | if (flen > maxlen) maxlen = flen; | ||
| 249 | if (vlen > maxlen) maxlen = vlen; | ||
| 250 | - zl = ziplistPush(zl, fstr, flen, ZIPLIST_TAIL); | ||
| 251 | - zl = ziplistPush(zl, vstr, vlen, ZIPLIST_TAIL); | ||
| 252 | |||
| 253 | /* search for duplicate records */ | ||
| 254 | sds field = sdstrynewlen(fstr, flen); | ||
| 255 | - if (!field || dictAdd(dupSearchDict, field, NULL) != DICT_OK) { | ||
| 256 | + if (!field || dictAdd(dupSearchDict, field, NULL) != DICT_OK || | ||
| 257 | + !ziplistSafeToAdd(zl, (size_t)flen + vlen)) { | ||
| 258 | rdbReportCorruptRDB("Hash zipmap with dup elements, or big length (%u)", flen); | ||
| 259 | dictRelease(dupSearchDict); | ||
| 260 | sdsfree(field); | ||
| 261 | @@ -1872,6 +1884,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 262 | decrRefCount(o); | ||
| 263 | return NULL; | ||
| 264 | } | ||
| 265 | + | ||
| 266 | + zl = ziplistPush(zl, fstr, flen, ZIPLIST_TAIL); | ||
| 267 | + zl = ziplistPush(zl, vstr, vlen, ZIPLIST_TAIL); | ||
| 268 | } | ||
| 269 | |||
| 270 | dictRelease(dupSearchDict); | ||
| 271 | diff --git a/src/server.h b/src/server.h | ||
| 272 | index d9fef9552..07b34c743 100644 | ||
| 273 | --- a/src/server.h | ||
| 274 | +++ b/src/server.h | ||
| 275 | @@ -2173,7 +2173,7 @@ unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range); | ||
| 276 | unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range); | ||
| 277 | unsigned long zsetLength(const robj *zobj); | ||
| 278 | void zsetConvert(robj *zobj, int encoding); | ||
| 279 | -void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen); | ||
| 280 | +void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen); | ||
| 281 | int zsetScore(robj *zobj, sds member, double *score); | ||
| 282 | unsigned long zslGetRank(zskiplist *zsl, double score, sds o); | ||
| 283 | int zsetAdd(robj *zobj, double score, sds ele, int in_flags, int *out_flags, double *newscore); | ||
| 284 | diff --git a/src/t_hash.c b/src/t_hash.c | ||
| 285 | index ea0606fb0..2720fdbc7 100644 | ||
| 286 | --- a/src/t_hash.c | ||
| 287 | +++ b/src/t_hash.c | ||
| 288 | @@ -39,17 +39,22 @@ | ||
| 289 | * as their string length can be queried in constant time. */ | ||
| 290 | void hashTypeTryConversion(robj *o, robj **argv, int start, int end) { | ||
| 291 | int i; | ||
| 292 | + size_t sum = 0; | ||
| 293 | |||
| 294 | if (o->encoding != OBJ_ENCODING_ZIPLIST) return; | ||
| 295 | |||
| 296 | for (i = start; i <= end; i++) { | ||
| 297 | - if (sdsEncodedObject(argv[i]) && | ||
| 298 | - sdslen(argv[i]->ptr) > server.hash_max_ziplist_value) | ||
| 299 | - { | ||
| 300 | + if (!sdsEncodedObject(argv[i])) | ||
| 301 | + continue; | ||
| 302 | + size_t len = sdslen(argv[i]->ptr); | ||
| 303 | + if (len > server.hash_max_ziplist_value) { | ||
| 304 | hashTypeConvert(o, OBJ_ENCODING_HT); | ||
| 305 | - break; | ||
| 306 | + return; | ||
| 307 | } | ||
| 308 | + sum += len; | ||
| 309 | } | ||
| 310 | + if (!ziplistSafeToAdd(o->ptr, sum)) | ||
| 311 | + hashTypeConvert(o, OBJ_ENCODING_HT); | ||
| 312 | } | ||
| 313 | |||
| 314 | /* Get the value from a ziplist encoded hash, identified by field. | ||
| 315 | diff --git a/src/t_list.c b/src/t_list.c | ||
| 316 | index f8ca27458..66c9e3c9d 100644 | ||
| 317 | --- a/src/t_list.c | ||
| 318 | +++ b/src/t_list.c | ||
| 319 | @@ -29,6 +29,8 @@ | ||
| 320 | |||
| 321 | #include "server.h" | ||
| 322 | |||
| 323 | +#define LIST_MAX_ITEM_SIZE ((1ull<<32)-1024) | ||
| 324 | + | ||
| 325 | /*----------------------------------------------------------------------------- | ||
| 326 | * List API | ||
| 327 | *----------------------------------------------------------------------------*/ | ||
| 328 | @@ -224,6 +226,13 @@ robj *listTypeDup(robj *o) { | ||
| 329 | void pushGenericCommand(client *c, int where, int xx) { | ||
| 330 | int j; | ||
| 331 | |||
| 332 | + for (j = 2; j < c->argc; j++) { | ||
| 333 | + if (sdslen(c->argv[j]->ptr) > LIST_MAX_ITEM_SIZE) { | ||
| 334 | + addReplyError(c, "Element too large"); | ||
| 335 | + return; | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | robj *lobj = lookupKeyWrite(c->db, c->argv[1]); | ||
| 340 | if (checkType(c,lobj,OBJ_LIST)) return; | ||
| 341 | if (!lobj) { | ||
| 342 | @@ -287,6 +296,11 @@ void linsertCommand(client *c) { | ||
| 343 | return; | ||
| 344 | } | ||
| 345 | |||
| 346 | + if (sdslen(c->argv[4]->ptr) > LIST_MAX_ITEM_SIZE) { | ||
| 347 | + addReplyError(c, "Element too large"); | ||
| 348 | + return; | ||
| 349 | + } | ||
| 350 | + | ||
| 351 | if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || | ||
| 352 | checkType(c,subject,OBJ_LIST)) return; | ||
| 353 | |||
| 354 | @@ -354,6 +368,11 @@ void lsetCommand(client *c) { | ||
| 355 | long index; | ||
| 356 | robj *value = c->argv[3]; | ||
| 357 | |||
| 358 | + if (sdslen(value->ptr) > LIST_MAX_ITEM_SIZE) { | ||
| 359 | + addReplyError(c, "Element too large"); | ||
| 360 | + return; | ||
| 361 | + } | ||
| 362 | + | ||
| 363 | if ((getLongFromObjectOrReply(c, c->argv[2], &index, NULL) != C_OK)) | ||
| 364 | return; | ||
| 365 | |||
| 366 | @@ -576,6 +595,11 @@ void lposCommand(client *c) { | ||
| 367 | int direction = LIST_TAIL; | ||
| 368 | long rank = 1, count = -1, maxlen = 0; /* Count -1: option not given. */ | ||
| 369 | |||
| 370 | + if (sdslen(ele->ptr) > LIST_MAX_ITEM_SIZE) { | ||
| 371 | + addReplyError(c, "Element too large"); | ||
| 372 | + return; | ||
| 373 | + } | ||
| 374 | + | ||
| 375 | /* Parse the optional arguments. */ | ||
| 376 | for (int j = 3; j < c->argc; j++) { | ||
| 377 | char *opt = c->argv[j]->ptr; | ||
| 378 | @@ -671,6 +695,11 @@ void lremCommand(client *c) { | ||
| 379 | long toremove; | ||
| 380 | long removed = 0; | ||
| 381 | |||
| 382 | + if (sdslen(obj->ptr) > LIST_MAX_ITEM_SIZE) { | ||
| 383 | + addReplyError(c, "Element too large"); | ||
| 384 | + return; | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | if ((getLongFromObjectOrReply(c, c->argv[2], &toremove, NULL) != C_OK)) | ||
| 388 | return; | ||
| 389 | |||
| 390 | diff --git a/src/t_stream.c b/src/t_stream.c | ||
| 391 | index 2c30faa06..574195ee3 100644 | ||
| 392 | --- a/src/t_stream.c | ||
| 393 | +++ b/src/t_stream.c | ||
| 394 | @@ -47,6 +47,12 @@ | ||
| 395 | * setting stream_node_max_bytes to a huge number. */ | ||
| 396 | #define STREAM_LISTPACK_MAX_PRE_ALLOCATE 4096 | ||
| 397 | |||
| 398 | +/* Don't let listpacks grow too big, even if the user config allows it. | ||
| 399 | + * doing so can lead to an overflow (trying to store more than 32bit length | ||
| 400 | + * into the listpack header), or actually an assertion since lpInsert | ||
| 401 | + * will return NULL. */ | ||
| 402 | +#define STREAM_LISTPACK_MAX_SIZE (1<<30) | ||
| 403 | + | ||
| 404 | void streamFreeCG(streamCG *cg); | ||
| 405 | void streamFreeNACK(streamNACK *na); | ||
| 406 | size_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start, streamID *end, size_t count, streamConsumer *consumer); | ||
| 407 | @@ -433,8 +439,11 @@ void streamGetEdgeID(stream *s, int first, streamID *edge_id) | ||
| 408 | * | ||
| 409 | * The function returns C_OK if the item was added, this is always true | ||
| 410 | * if the ID was generated by the function. However the function may return | ||
| 411 | - * C_ERR if an ID was given via 'use_id', but adding it failed since the | ||
| 412 | - * current top ID is greater or equal. */ | ||
| 413 | + * C_ERR in several cases: | ||
| 414 | + * 1. If an ID was given via 'use_id', but adding it failed since the | ||
| 415 | + * current top ID is greater or equal. errno will be set to EDOM. | ||
| 416 | + * 2. If a size of a single element or the sum of the elements is too big to | ||
| 417 | + * be stored into the stream. errno will be set to ERANGE. */ | ||
| 418 | int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_id, streamID *use_id) { | ||
| 419 | |||
| 420 | /* Generate the new entry ID. */ | ||
| 421 | @@ -448,7 +457,23 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_ | ||
| 422 | * or return an error. Automatically generated IDs might | ||
| 423 | * overflow (and wrap-around) when incrementing the sequence | ||
| 424 | part. */ | ||
| 425 | - if (streamCompareID(&id,&s->last_id) <= 0) return C_ERR; | ||
| 426 | + if (streamCompareID(&id,&s->last_id) <= 0) { | ||
| 427 | + errno = EDOM; | ||
| 428 | + return C_ERR; | ||
| 429 | + } | ||
| 430 | + | ||
| 431 | + /* Avoid overflow when trying to add an element to the stream (listpack | ||
| 432 | + * can only host up to 32bit length sttrings, and also a total listpack size | ||
| 433 | + * can't be bigger than 32bit length. */ | ||
| 434 | + size_t totelelen = 0; | ||
| 435 | + for (int64_t i = 0; i < numfields*2; i++) { | ||
| 436 | + sds ele = argv[i]->ptr; | ||
| 437 | + totelelen += sdslen(ele); | ||
| 438 | + } | ||
| 439 | + if (totelelen > STREAM_LISTPACK_MAX_SIZE) { | ||
| 440 | + errno = ERANGE; | ||
| 441 | + return C_ERR; | ||
| 442 | + } | ||
| 443 | |||
| 444 | /* Add the new entry. */ | ||
| 445 | raxIterator ri; | ||
| 446 | @@ -507,9 +532,10 @@ int streamAppendItem(stream *s, robj **argv, int64_t numfields, streamID *added_ | ||
| 447 | * if we need to switch to the next one. 'lp' will be set to NULL if | ||
| 448 | * the current node is full. */ | ||
| 449 | if (lp != NULL) { | ||
| 450 | - if (server.stream_node_max_bytes && | ||
| 451 | - lp_bytes >= server.stream_node_max_bytes) | ||
| 452 | - { | ||
| 453 | + size_t node_max_bytes = server.stream_node_max_bytes; | ||
| 454 | + if (node_max_bytes == 0 || node_max_bytes > STREAM_LISTPACK_MAX_SIZE) | ||
| 455 | + node_max_bytes = STREAM_LISTPACK_MAX_SIZE; | ||
| 456 | + if (lp_bytes + totelelen >= node_max_bytes) { | ||
| 457 | lp = NULL; | ||
| 458 | } else if (server.stream_node_max_entries) { | ||
| 459 | unsigned char *lp_ele = lpFirst(lp); | ||
| 460 | @@ -1796,11 +1822,13 @@ void xaddCommand(client *c) { | ||
| 461 | /* Append using the low level function and return the ID. */ | ||
| 462 | streamID id; | ||
| 463 | if (streamAppendItem(s,c->argv+field_pos,(c->argc-field_pos)/2, | ||
| 464 | - &id, parsed_args.id_given ? &parsed_args.id : NULL) | ||
| 465 | - == C_ERR) | ||
| 466 | + &id, parsed_args.id_given ? &parsed_args.id : NULL) == C_ERR) | ||
| 467 | { | ||
| 468 | - addReplyError(c,"The ID specified in XADD is equal or smaller than the " | ||
| 469 | - "target stream top item"); | ||
| 470 | + if (errno == EDOM) | ||
| 471 | + addReplyError(c,"The ID specified in XADD is equal or smaller than " | ||
| 472 | + "the target stream top item"); | ||
| 473 | + else | ||
| 474 | + addReplyError(c,"Elements are too large to be stored"); | ||
| 475 | return; | ||
| 476 | } | ||
| 477 | addReplyStreamID(c,&id); | ||
| 478 | diff --git a/src/t_zset.c b/src/t_zset.c | ||
| 479 | index 3b9ebd2bd..2abc1b49b 100644 | ||
| 480 | --- a/src/t_zset.c | ||
| 481 | +++ b/src/t_zset.c | ||
| 482 | @@ -1242,15 +1242,18 @@ void zsetConvert(robj *zobj, int encoding) { | ||
| 483 | } | ||
| 484 | |||
| 485 | /* Convert the sorted set object into a ziplist if it is not already a ziplist | ||
| 486 | - * and if the number of elements and the maximum element size is within the | ||
| 487 | - * expected ranges. */ | ||
| 488 | -void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) { | ||
| 489 | + * and if the number of elements and the maximum element size and total elements size | ||
| 490 | + * are within the expected ranges. */ | ||
| 491 | +void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen, size_t totelelen) { | ||
| 492 | if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return; | ||
| 493 | zset *zset = zobj->ptr; | ||
| 494 | |||
| 495 | if (zset->zsl->length <= server.zset_max_ziplist_entries && | ||
| 496 | - maxelelen <= server.zset_max_ziplist_value) | ||
| 497 | - zsetConvert(zobj,OBJ_ENCODING_ZIPLIST); | ||
| 498 | + maxelelen <= server.zset_max_ziplist_value && | ||
| 499 | + ziplistSafeToAdd(NULL, totelelen)) | ||
| 500 | + { | ||
| 501 | + zsetConvert(zobj,OBJ_ENCODING_ZIPLIST); | ||
| 502 | + } | ||
| 503 | } | ||
| 504 | |||
| 505 | /* Return (by reference) the score of the specified member of the sorted set | ||
| 506 | @@ -1370,20 +1373,28 @@ int zsetAdd(robj *zobj, double score, sds ele, int in_flags, int *out_flags, dou | ||
| 507 | } | ||
| 508 | return 1; | ||
| 509 | } else if (!xx) { | ||
| 510 | - /* Optimize: check if the element is too large or the list | ||
| 511 | + /* check if the element is too large or the list | ||
| 512 | * becomes too long *before* executing zzlInsert. */ | ||
| 513 | - zobj->ptr = zzlInsert(zobj->ptr,ele,score); | ||
| 514 | - if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries || | ||
| 515 | - sdslen(ele) > server.zset_max_ziplist_value) | ||
| 516 | + if (zzlLength(zobj->ptr)+1 > server.zset_max_ziplist_entries || | ||
| 517 | + sdslen(ele) > server.zset_max_ziplist_value || | ||
| 518 | + !ziplistSafeToAdd(zobj->ptr, sdslen(ele))) | ||
| 519 | + { | ||
| 520 | zsetConvert(zobj,OBJ_ENCODING_SKIPLIST); | ||
| 521 | - if (newscore) *newscore = score; | ||
| 522 | - *out_flags |= ZADD_OUT_ADDED; | ||
| 523 | - return 1; | ||
| 524 | + } else { | ||
| 525 | + zobj->ptr = zzlInsert(zobj->ptr,ele,score); | ||
| 526 | + if (newscore) *newscore = score; | ||
| 527 | + *out_flags |= ZADD_OUT_ADDED; | ||
| 528 | + return 1; | ||
| 529 | + } | ||
| 530 | } else { | ||
| 531 | *out_flags |= ZADD_OUT_NOP; | ||
| 532 | return 1; | ||
| 533 | } | ||
| 534 | - } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 535 | + } | ||
| 536 | + | ||
| 537 | + /* Note that the above block handling ziplist would have either returned or | ||
| 538 | + * converted the key to skiplist. */ | ||
| 539 | + if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { | ||
| 540 | zset *zs = zobj->ptr; | ||
| 541 | zskiplistNode *znode; | ||
| 542 | dictEntry *de; | ||
| 543 | @@ -2361,7 +2372,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | -static int zsetDictGetMaxElementLength(dict *d) { | ||
| 548 | +static size_t zsetDictGetMaxElementLength(dict *d, size_t *totallen) { | ||
| 549 | dictIterator *di; | ||
| 550 | dictEntry *de; | ||
| 551 | size_t maxelelen = 0; | ||
| 552 | @@ -2371,6 +2382,8 @@ static int zsetDictGetMaxElementLength(dict *d) { | ||
| 553 | while((de = dictNext(di)) != NULL) { | ||
| 554 | sds ele = dictGetKey(de); | ||
| 555 | if (sdslen(ele) > maxelelen) maxelelen = sdslen(ele); | ||
| 556 | + if (totallen) | ||
| 557 | + (*totallen) += sdslen(ele); | ||
| 558 | } | ||
| 559 | |||
| 560 | dictReleaseIterator(di); | ||
| 561 | @@ -2378,7 +2391,7 @@ static int zsetDictGetMaxElementLength(dict *d) { | ||
| 562 | return maxelelen; | ||
| 563 | } | ||
| 564 | |||
| 565 | -static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) { | ||
| 566 | +static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) { | ||
| 567 | /* DIFF Algorithm 1: | ||
| 568 | * | ||
| 569 | * We perform the diff by iterating all the elements of the first set, | ||
| 570 | @@ -2426,13 +2439,14 @@ static void zdiffAlgorithm1(zsetopsrc *src, long setnum, zset *dstzset, size_t * | ||
| 571 | znode = zslInsert(dstzset->zsl,zval.score,tmp); | ||
| 572 | dictAdd(dstzset->dict,tmp,&znode->score); | ||
| 573 | if (sdslen(tmp) > *maxelelen) *maxelelen = sdslen(tmp); | ||
| 574 | + (*totelelen) += sdslen(tmp); | ||
| 575 | } | ||
| 576 | } | ||
| 577 | zuiClearIterator(&src[0]); | ||
| 578 | } | ||
| 579 | |||
| 580 | |||
| 581 | -static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) { | ||
| 582 | +static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) { | ||
| 583 | /* DIFF Algorithm 2: | ||
| 584 | * | ||
| 585 | * Add all the elements of the first set to the auxiliary set. | ||
| 586 | @@ -2486,7 +2500,7 @@ static void zdiffAlgorithm2(zsetopsrc *src, long setnum, zset *dstzset, size_t * | ||
| 587 | |||
| 588 | /* Using this algorithm, we can't calculate the max element as we go, | ||
| 589 | * we have to iterate through all elements to find the max one after. */ | ||
| 590 | - *maxelelen = zsetDictGetMaxElementLength(dstzset->dict); | ||
| 591 | + *maxelelen = zsetDictGetMaxElementLength(dstzset->dict, totelelen); | ||
| 592 | } | ||
| 593 | |||
| 594 | static int zsetChooseDiffAlgorithm(zsetopsrc *src, long setnum) { | ||
| 595 | @@ -2523,14 +2537,14 @@ static int zsetChooseDiffAlgorithm(zsetopsrc *src, long setnum) { | ||
| 596 | return (algo_one_work <= algo_two_work) ? 1 : 2; | ||
| 597 | } | ||
| 598 | |||
| 599 | -static void zdiff(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen) { | ||
| 600 | +static void zdiff(zsetopsrc *src, long setnum, zset *dstzset, size_t *maxelelen, size_t *totelelen) { | ||
| 601 | /* Skip everything if the smallest input is empty. */ | ||
| 602 | if (zuiLength(&src[0]) > 0) { | ||
| 603 | int diff_algo = zsetChooseDiffAlgorithm(src, setnum); | ||
| 604 | if (diff_algo == 1) { | ||
| 605 | - zdiffAlgorithm1(src, setnum, dstzset, maxelelen); | ||
| 606 | + zdiffAlgorithm1(src, setnum, dstzset, maxelelen, totelelen); | ||
| 607 | } else if (diff_algo == 2) { | ||
| 608 | - zdiffAlgorithm2(src, setnum, dstzset, maxelelen); | ||
| 609 | + zdiffAlgorithm2(src, setnum, dstzset, maxelelen, totelelen); | ||
| 610 | } else if (diff_algo != 0) { | ||
| 611 | serverPanic("Unknown algorithm"); | ||
| 612 | } | ||
| 613 | @@ -2565,7 +2579,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in | ||
| 614 | zsetopsrc *src; | ||
| 615 | zsetopval zval; | ||
| 616 | sds tmp; | ||
| 617 | - size_t maxelelen = 0; | ||
| 618 | + size_t maxelelen = 0, totelelen = 0; | ||
| 619 | robj *dstobj; | ||
| 620 | zset *dstzset; | ||
| 621 | zskiplistNode *znode; | ||
| 622 | @@ -2701,6 +2715,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in | ||
| 623 | tmp = zuiNewSdsFromValue(&zval); | ||
| 624 | znode = zslInsert(dstzset->zsl,score,tmp); | ||
| 625 | dictAdd(dstzset->dict,tmp,&znode->score); | ||
| 626 | + totelelen += sdslen(tmp); | ||
| 627 | if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp); | ||
| 628 | } | ||
| 629 | } | ||
| 630 | @@ -2737,6 +2752,7 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in | ||
| 631 | /* Remember the longest single element encountered, | ||
| 632 | * to understand if it's possible to convert to ziplist | ||
| 633 | * at the end. */ | ||
| 634 | + totelelen += sdslen(tmp); | ||
| 635 | if (sdslen(tmp) > maxelelen) maxelelen = sdslen(tmp); | ||
| 636 | /* Update the element with its initial score. */ | ||
| 637 | dictSetKey(accumulator, de, tmp); | ||
| 638 | @@ -2771,14 +2787,14 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in | ||
| 639 | dictReleaseIterator(di); | ||
| 640 | dictRelease(accumulator); | ||
| 641 | } else if (op == SET_OP_DIFF) { | ||
| 642 | - zdiff(src, setnum, dstzset, &maxelelen); | ||
| 643 | + zdiff(src, setnum, dstzset, &maxelelen, &totelelen); | ||
| 644 | } else { | ||
| 645 | serverPanic("Unknown operator"); | ||
| 646 | } | ||
| 647 | |||
| 648 | if (dstkey) { | ||
| 649 | if (dstzset->zsl->length) { | ||
| 650 | - zsetConvertToZiplistIfNeeded(dstobj, maxelelen); | ||
| 651 | + zsetConvertToZiplistIfNeeded(dstobj, maxelelen, totelelen); | ||
| 652 | setKey(c, c->db, dstkey, dstobj); | ||
| 653 | addReplyLongLong(c, zsetLength(dstobj)); | ||
| 654 | notifyKeyspaceEvent(NOTIFY_ZSET, | ||
| 655 | diff --git a/src/ziplist.c b/src/ziplist.c | ||
| 656 | index aae86c1f2..fdc1bb9e1 100644 | ||
| 657 | --- a/src/ziplist.c | ||
| 658 | +++ b/src/ziplist.c | ||
| 659 | @@ -267,6 +267,17 @@ | ||
| 660 | ZIPLIST_LENGTH(zl) = intrev16ifbe(intrev16ifbe(ZIPLIST_LENGTH(zl))+incr); \ | ||
| 661 | } | ||
| 662 | |||
| 663 | +/* Don't let ziplists grow over 1GB in any case, don't wanna risk overflow in | ||
| 664 | + * zlbytes*/ | ||
| 665 | +#define ZIPLIST_MAX_SAFETY_SIZE (1<<30) | ||
| 666 | +int ziplistSafeToAdd(unsigned char* zl, size_t add) { | ||
| 667 | + size_t len = zl? ziplistBlobLen(zl): 0; | ||
| 668 | + if (len + add > ZIPLIST_MAX_SAFETY_SIZE) | ||
| 669 | + return 0; | ||
| 670 | + return 1; | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | + | ||
| 674 | /* We use this function to receive information about a ziplist entry. | ||
| 675 | * Note that this is not how the data is actually encoded, is just what we | ||
| 676 | * get filled by a function in order to operate more easily. */ | ||
| 677 | @@ -709,7 +720,8 @@ unsigned char *ziplistNew(void) { | ||
| 678 | } | ||
| 679 | |||
| 680 | /* Resize the ziplist. */ | ||
| 681 | -unsigned char *ziplistResize(unsigned char *zl, unsigned int len) { | ||
| 682 | +unsigned char *ziplistResize(unsigned char *zl, size_t len) { | ||
| 683 | + assert(len < UINT32_MAX); | ||
| 684 | zl = zrealloc(zl,len); | ||
| 685 | ZIPLIST_BYTES(zl) = intrev32ifbe(len); | ||
| 686 | zl[len-1] = ZIP_END; | ||
| 687 | @@ -1070,6 +1082,9 @@ unsigned char *ziplistMerge(unsigned char **first, unsigned char **second) { | ||
| 688 | /* Combined zl length should be limited within UINT16_MAX */ | ||
| 689 | zllength = zllength < UINT16_MAX ? zllength : UINT16_MAX; | ||
| 690 | |||
| 691 | + /* larger values can't be stored into ZIPLIST_BYTES */ | ||
| 692 | + assert(zlbytes < UINT32_MAX); | ||
| 693 | + | ||
| 694 | /* Save offset positions before we start ripping memory apart. */ | ||
| 695 | size_t first_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*first)); | ||
| 696 | size_t second_offset = intrev32ifbe(ZIPLIST_TAIL_OFFSET(*second)); | ||
| 697 | diff --git a/src/ziplist.h b/src/ziplist.h | ||
| 698 | index 9e7997ad8..569e1259d 100644 | ||
| 699 | --- a/src/ziplist.h | ||
| 700 | +++ b/src/ziplist.h | ||
| 701 | @@ -65,6 +65,7 @@ int ziplistValidateIntegrity(unsigned char *zl, size_t size, int deep, | ||
| 702 | void ziplistRandomPair(unsigned char *zl, unsigned long total_count, ziplistEntry *key, ziplistEntry *val); | ||
| 703 | void ziplistRandomPairs(unsigned char *zl, unsigned int count, ziplistEntry *keys, ziplistEntry *vals); | ||
| 704 | unsigned int ziplistRandomPairsUnique(unsigned char *zl, unsigned int count, ziplistEntry *keys, ziplistEntry *vals); | ||
| 705 | +int ziplistSafeToAdd(unsigned char* zl, size_t add); | ||
| 706 | |||
| 707 | #ifdef REDIS_TEST | ||
| 708 | int ziplistTest(int argc, char *argv[], int accurate); | ||
| 709 | diff --git a/tests/unit/violations.tcl b/tests/unit/violations.tcl | ||
| 710 | new file mode 100644 | ||
| 711 | index 000000000..1d3140c52 | ||
| 712 | --- /dev/null | ||
| 713 | +++ b/tests/unit/violations.tcl | ||
| 714 | @@ -0,0 +1,156 @@ | ||
| 715 | +# These tests consume massive amounts of memory, and are not | ||
| 716 | +# suitable to be executed as part of the normal test suite | ||
| 717 | +set ::str500 [string repeat x 500000000] ;# 500mb | ||
| 718 | + | ||
| 719 | +# Utility function to write big argument into redis client connection | ||
| 720 | +proc write_big_bulk {size} { | ||
| 721 | + r write "\$$size\r\n" | ||
| 722 | + while {$size >= 500000000} { | ||
| 723 | + r write $::str500 | ||
| 724 | + incr size -500000000 | ||
| 725 | + } | ||
| 726 | + if {$size > 0} { | ||
| 727 | + r write [string repeat x $size] | ||
| 728 | + } | ||
| 729 | + r write "\r\n" | ||
| 730 | +} | ||
| 731 | + | ||
| 732 | +# One XADD with one huge 5GB field | ||
| 733 | +# Expected to fail resulting in an empty stream | ||
| 734 | +start_server [list overrides [list save ""] ] { | ||
| 735 | + test {XADD one huge field} { | ||
| 736 | + r config set proto-max-bulk-len 10000000000 ;#10gb | ||
| 737 | + r config set client-query-buffer-limit 10000000000 ;#10gb | ||
| 738 | + r write "*5\r\n\$4\r\nXADD\r\n\$2\r\nS1\r\n\$1\r\n*\r\n" | ||
| 739 | + r write "\$1\r\nA\r\n" | ||
| 740 | + write_big_bulk 5000000000 ;#5gb | ||
| 741 | + r flush | ||
| 742 | + catch {r read} err | ||
| 743 | + assert_match {*too large*} $err | ||
| 744 | + r xlen S1 | ||
| 745 | + } {0} | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | +# One XADD with one huge (exactly nearly) 4GB field | ||
| 749 | +# This uncovers the overflow in lpEncodeGetType | ||
| 750 | +# Expected to fail resulting in an empty stream | ||
| 751 | +start_server [list overrides [list save ""] ] { | ||
| 752 | + test {XADD one huge field - 1} { | ||
| 753 | + r config set proto-max-bulk-len 10000000000 ;#10gb | ||
| 754 | + r config set client-query-buffer-limit 10000000000 ;#10gb | ||
| 755 | + r write "*5\r\n\$4\r\nXADD\r\n\$2\r\nS1\r\n\$1\r\n*\r\n" | ||
| 756 | + r write "\$1\r\nA\r\n" | ||
| 757 | + write_big_bulk 4294967295 ;#4gb-1 | ||
| 758 | + r flush | ||
| 759 | + catch {r read} err | ||
| 760 | + assert_match {*too large*} $err | ||
| 761 | + r xlen S1 | ||
| 762 | + } {0} | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +# Gradually add big stream fields using repeated XADD calls | ||
| 766 | +start_server [list overrides [list save ""] ] { | ||
| 767 | + test {several XADD big fields} { | ||
| 768 | + r config set stream-node-max-bytes 0 | ||
| 769 | + for {set j 0} {$j<10} {incr j} { | ||
| 770 | + r xadd stream * 1 $::str500 2 $::str500 | ||
| 771 | + } | ||
| 772 | + r ping | ||
| 773 | + r xlen stream | ||
| 774 | + } {10} | ||
| 775 | +} | ||
| 776 | + | ||
| 777 | +# Add over 4GB to a single stream listpack (one XADD command) | ||
| 778 | +# Expected to fail resulting in an empty stream | ||
| 779 | +start_server [list overrides [list save ""] ] { | ||
| 780 | + test {single XADD big fields} { | ||
| 781 | + r write "*23\r\n\$4\r\nXADD\r\n\$1\r\nS\r\n\$1\r\n*\r\n" | ||
| 782 | + for {set j 0} {$j<10} {incr j} { | ||
| 783 | + r write "\$1\r\n$j\r\n" | ||
| 784 | + write_big_bulk 500000000 ;#500mb | ||
| 785 | + } | ||
| 786 | + r flush | ||
| 787 | + catch {r read} err | ||
| 788 | + assert_match {*too large*} $err | ||
| 789 | + r xlen S | ||
| 790 | + } {0} | ||
| 791 | +} | ||
| 792 | + | ||
| 793 | +# Gradually add big hash fields using repeated HSET calls | ||
| 794 | +# This reproduces the overflow in the call to ziplistResize | ||
| 795 | +# Object will be converted to hashtable encoding | ||
| 796 | +start_server [list overrides [list save ""] ] { | ||
| 797 | + r config set hash-max-ziplist-value 1000000000 ;#1gb | ||
| 798 | + test {hash with many big fields} { | ||
| 799 | + for {set j 0} {$j<10} {incr j} { | ||
| 800 | + r hset h $j $::str500 | ||
| 801 | + } | ||
| 802 | + r object encoding h | ||
| 803 | + } {hashtable} | ||
| 804 | +} | ||
| 805 | + | ||
| 806 | +# Add over 4GB to a single hash field (one HSET command) | ||
| 807 | +# Object will be converted to hashtable encoding | ||
| 808 | +start_server [list overrides [list save ""] ] { | ||
| 809 | + test {hash with one huge field} { | ||
| 810 | + catch {r config set hash-max-ziplist-value 10000000000} ;#10gb | ||
| 811 | + r config set proto-max-bulk-len 10000000000 ;#10gb | ||
| 812 | + r config set client-query-buffer-limit 10000000000 ;#10gb | ||
| 813 | + r write "*4\r\n\$4\r\nHSET\r\n\$2\r\nH1\r\n" | ||
| 814 | + r write "\$1\r\nA\r\n" | ||
| 815 | + write_big_bulk 5000000000 ;#5gb | ||
| 816 | + r flush | ||
| 817 | + r read | ||
| 818 | + r object encoding H1 | ||
| 819 | + } {hashtable} | ||
| 820 | +} | ||
| 821 | + | ||
| 822 | +# Add over 4GB to a single list member (one LPUSH command) | ||
| 823 | +# Currently unsupported, and expected to fail rather than being truncated | ||
| 824 | +# Expected to fail resulting in a non-existing list | ||
| 825 | +start_server [list overrides [list save ""] ] { | ||
| 826 | + test {list with one huge field} { | ||
| 827 | + r config set proto-max-bulk-len 10000000000 ;#10gb | ||
| 828 | + r config set client-query-buffer-limit 10000000000 ;#10gb | ||
| 829 | + r write "*3\r\n\$5\r\nLPUSH\r\n\$2\r\nL1\r\n" | ||
| 830 | + write_big_bulk 5000000000 ;#5gb | ||
| 831 | + r flush | ||
| 832 | + catch {r read} err | ||
| 833 | + assert_match {*too large*} $err | ||
| 834 | + r exists L1 | ||
| 835 | + } {0} | ||
| 836 | +} | ||
| 837 | + | ||
| 838 | +# SORT which attempts to store an element larger than 4GB into a list. | ||
| 839 | +# Currently unsupported and results in an assertion instead of truncation | ||
| 840 | +start_server [list overrides [list save ""] ] { | ||
| 841 | + test {SORT adds huge field to list} { | ||
| 842 | + r config set proto-max-bulk-len 10000000000 ;#10gb | ||
| 843 | + r config set client-query-buffer-limit 10000000000 ;#10gb | ||
| 844 | + r write "*3\r\n\$3\r\nSET\r\n\$2\r\nS1\r\n" | ||
| 845 | + write_big_bulk 5000000000 ;#5gb | ||
| 846 | + r flush | ||
| 847 | + r read | ||
| 848 | + assert_equal [r strlen S1] 5000000000 | ||
| 849 | + r set S2 asdf | ||
| 850 | + r sadd myset 1 2 | ||
| 851 | + r mset D1 1 D2 2 | ||
| 852 | + catch {r sort myset by D* get S* store mylist} | ||
| 853 | + assert_equal [count_log_message 0 "crashed by signal"] 0 | ||
| 854 | + assert_equal [count_log_message 0 "ASSERTION FAILED"] 1 | ||
| 855 | + } | ||
| 856 | +} | ||
| 857 | + | ||
| 858 | +# SORT which stores an integer encoded element into a list. | ||
| 859 | +# Just for coverage, no news here. | ||
| 860 | +start_server [list overrides [list save ""] ] { | ||
| 861 | + test {SORT adds integer field to list} { | ||
| 862 | + r set S1 asdf | ||
| 863 | + r set S2 123 ;# integer encoded | ||
| 864 | + assert_encoding "int" S2 | ||
| 865 | + r sadd myset 1 2 | ||
| 866 | + r mset D1 1 D2 2 | ||
| 867 | + r sort myset by D* get S* store mylist | ||
| 868 | + r llen mylist | ||
| 869 | + } {2} | ||
| 870 | +} | ||
| 871 | -- | ||
| 872 | 2.17.1 | ||
| 873 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32675.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32675.patch deleted file mode 100644 index ab691612a9..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32675.patch +++ /dev/null | |||
| @@ -1,129 +0,0 @@ | |||
| 1 | From a71a65e9ed75b347c33bc882b38f4f1006fcba39 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Wed, 9 Jun 2021 17:31:39 +0300 | ||
| 4 | Subject: [PATCH] Prevent unauthenticated client from easily consuming lots of | ||
| 5 | memory (CVE-2021-32675) | ||
| 6 | |||
| 7 | This change sets a low limit for multibulk and bulk length in the | ||
| 8 | protocol for unauthenticated connections, so that they can't easily | ||
| 9 | cause redis to allocate massive amounts of memory by sending just a few | ||
| 10 | characters on the network. | ||
| 11 | The new limits are 10 arguments of 16kb each (instead of 1m of 512mb) | ||
| 12 | |||
| 13 | CVE: CVE-2021-32675 | ||
| 14 | Upstream-Status: Backport[https://github.com/redis/redis/commit/5674b0057ff2903d43eaff802017eddf37c360f8] | ||
| 15 | |||
| 16 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 17 | --- | ||
| 18 | src/networking.c | 17 +++++++++++++++++ | ||
| 19 | src/server.c | 11 +++-------- | ||
| 20 | src/server.h | 1 + | ||
| 21 | tests/unit/auth.tcl | 16 ++++++++++++++++ | ||
| 22 | 4 files changed, 37 insertions(+), 8 deletions(-) | ||
| 23 | |||
| 24 | diff --git a/src/networking.c b/src/networking.c | ||
| 25 | index 2355a37..8e891c6 100644 | ||
| 26 | --- a/src/networking.c | ||
| 27 | +++ b/src/networking.c | ||
| 28 | @@ -107,6 +107,15 @@ static void clientSetDefaultAuth(client *c) { | ||
| 29 | !(c->user->flags & USER_FLAG_DISABLED); | ||
| 30 | } | ||
| 31 | |||
| 32 | +int authRequired(client *c) { | ||
| 33 | + /* Check if the user is authenticated. This check is skipped in case | ||
| 34 | + * the default user is flagged as "nopass" and is active. */ | ||
| 35 | + int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) || | ||
| 36 | + (DefaultUser->flags & USER_FLAG_DISABLED)) && | ||
| 37 | + !c->authenticated; | ||
| 38 | + return auth_required; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | client *createClient(connection *conn) { | ||
| 42 | client *c = zmalloc(sizeof(client)); | ||
| 43 | |||
| 44 | @@ -1855,6 +1864,10 @@ int processMultibulkBuffer(client *c) { | ||
| 45 | addReplyError(c,"Protocol error: invalid multibulk length"); | ||
| 46 | setProtocolError("invalid mbulk count",c); | ||
| 47 | return C_ERR; | ||
| 48 | + } else if (ll > 10 && authRequired(c)) { | ||
| 49 | + addReplyError(c, "Protocol error: unauthenticated multibulk length"); | ||
| 50 | + setProtocolError("unauth mbulk count", c); | ||
| 51 | + return C_ERR; | ||
| 52 | } | ||
| 53 | |||
| 54 | c->qb_pos = (newline-c->querybuf)+2; | ||
| 55 | @@ -1902,6 +1915,10 @@ int processMultibulkBuffer(client *c) { | ||
| 56 | addReplyError(c,"Protocol error: invalid bulk length"); | ||
| 57 | setProtocolError("invalid bulk length",c); | ||
| 58 | return C_ERR; | ||
| 59 | + } else if (ll > 16384 && authRequired(c)) { | ||
| 60 | + addReplyError(c, "Protocol error: unauthenticated bulk length"); | ||
| 61 | + setProtocolError("unauth bulk length", c); | ||
| 62 | + return C_ERR; | ||
| 63 | } | ||
| 64 | |||
| 65 | c->qb_pos = newline-c->querybuf+2; | ||
| 66 | diff --git a/src/server.c b/src/server.c | ||
| 67 | index 9932606..f65ad22 100644 | ||
| 68 | --- a/src/server.c | ||
| 69 | +++ b/src/server.c | ||
| 70 | @@ -3996,14 +3996,9 @@ int processCommand(client *c) { | ||
| 71 | int is_may_replicate_command = (c->cmd->flags & (CMD_WRITE | CMD_MAY_REPLICATE)) || | ||
| 72 | (c->cmd->proc == execCommand && (c->mstate.cmd_flags & (CMD_WRITE | CMD_MAY_REPLICATE))); | ||
| 73 | |||
| 74 | - /* Check if the user is authenticated. This check is skipped in case | ||
| 75 | - * the default user is flagged as "nopass" and is active. */ | ||
| 76 | - int auth_required = (!(DefaultUser->flags & USER_FLAG_NOPASS) || | ||
| 77 | - (DefaultUser->flags & USER_FLAG_DISABLED)) && | ||
| 78 | - !c->authenticated; | ||
| 79 | - if (auth_required) { | ||
| 80 | - /* AUTH and HELLO and no auth modules are valid even in | ||
| 81 | - * non-authenticated state. */ | ||
| 82 | + if (authRequired(c)) { | ||
| 83 | + /* AUTH and HELLO and no auth commands are valid even in | ||
| 84 | + * non-authenticated state. */ | ||
| 85 | if (!(c->cmd->flags & CMD_NO_AUTH)) { | ||
| 86 | rejectCommand(c,shared.noautherr); | ||
| 87 | return C_OK; | ||
| 88 | diff --git a/src/server.h b/src/server.h | ||
| 89 | index e256ce0..a3dfe60 100644 | ||
| 90 | --- a/src/server.h | ||
| 91 | +++ b/src/server.h | ||
| 92 | @@ -1894,6 +1894,7 @@ void protectClient(client *c); | ||
| 93 | void unprotectClient(client *c); | ||
| 94 | void initThreadedIO(void); | ||
| 95 | client *lookupClientByID(uint64_t id); | ||
| 96 | +int authRequired(client *c); | ||
| 97 | |||
| 98 | #ifdef __GNUC__ | ||
| 99 | void addReplyErrorFormat(client *c, const char *fmt, ...) | ||
| 100 | diff --git a/tests/unit/auth.tcl b/tests/unit/auth.tcl | ||
| 101 | index b63cf01..5997707 100644 | ||
| 102 | --- a/tests/unit/auth.tcl | ||
| 103 | +++ b/tests/unit/auth.tcl | ||
| 104 | @@ -24,6 +24,22 @@ start_server {tags {"auth"} overrides {requirepass foobar}} { | ||
| 105 | r set foo 100 | ||
| 106 | r incr foo | ||
| 107 | } {101} | ||
| 108 | + | ||
| 109 | + test {For unauthenticated clients multibulk and bulk length are limited} { | ||
| 110 | + set rr [redis [srv "host"] [srv "port"] 0 $::tls] | ||
| 111 | + $rr write "*100\r\n" | ||
| 112 | + $rr flush | ||
| 113 | + catch {[$rr read]} e | ||
| 114 | + assert_match {*unauthenticated multibulk length*} $e | ||
| 115 | + $rr close | ||
| 116 | + | ||
| 117 | + set rr [redis [srv "host"] [srv "port"] 0 $::tls] | ||
| 118 | + $rr write "*1\r\n\$100000000\r\n" | ||
| 119 | + $rr flush | ||
| 120 | + catch {[$rr read]} e | ||
| 121 | + assert_match {*unauthenticated bulk length*} $e | ||
| 122 | + $rr close | ||
| 123 | + } | ||
| 124 | } | ||
| 125 | |||
| 126 | start_server {tags {"auth_binary_password"}} { | ||
| 127 | -- | ||
| 128 | 2.17.1 | ||
| 129 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32687.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32687.patch deleted file mode 100644 index fe04e67f30..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32687.patch +++ /dev/null | |||
| @@ -1,67 +0,0 @@ | |||
| 1 | From a40ee258accdaf56c23950a6371307ca1aa69f06 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Sun, 26 Sep 2021 15:42:17 +0300 | ||
| 4 | Subject: [PATCH] Fix Integer overflow issue with intsets (CVE-2021-32687) | ||
| 5 | |||
| 6 | The vulnerability involves changing the default set-max-intset-entries | ||
| 7 | configuration parameter to a very large value and constructing specially | ||
| 8 | crafted commands to manipulate sets | ||
| 9 | |||
| 10 | CVE: CVE-2021-32687 | ||
| 11 | Upstream-Status: Backport[https://github.com/redis/redis/commit/a30d367a71b7017581cf1ca104242a3c644dec0f] | ||
| 12 | |||
| 13 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 14 | --- | ||
| 15 | src/intset.c | 3 ++- | ||
| 16 | src/rdb.c | 4 +++- | ||
| 17 | src/t_set.c | 5 ++++- | ||
| 18 | 3 files changed, 9 insertions(+), 3 deletions(-) | ||
| 19 | |||
| 20 | diff --git a/src/intset.c b/src/intset.c | ||
| 21 | index 9ba1389..e366851 100644 | ||
| 22 | --- a/src/intset.c | ||
| 23 | +++ b/src/intset.c | ||
| 24 | @@ -104,7 +104,8 @@ intset *intsetNew(void) { | ||
| 25 | |||
| 26 | /* Resize the intset */ | ||
| 27 | static intset *intsetResize(intset *is, uint32_t len) { | ||
| 28 | - uint32_t size = len*intrev32ifbe(is->encoding); | ||
| 29 | + uint64_t size = (uint64_t)len*intrev32ifbe(is->encoding); | ||
| 30 | + assert(size <= SIZE_MAX - sizeof(intset)); | ||
| 31 | is = zrealloc(is,sizeof(intset)+size); | ||
| 32 | return is; | ||
| 33 | } | ||
| 34 | diff --git a/src/rdb.c b/src/rdb.c | ||
| 35 | index 6f2f516..37b1e0b 100644 | ||
| 36 | --- a/src/rdb.c | ||
| 37 | +++ b/src/rdb.c | ||
| 38 | @@ -1562,7 +1562,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) { | ||
| 39 | if ((len = rdbLoadLen(rdb,NULL)) == RDB_LENERR) return NULL; | ||
| 40 | |||
| 41 | /* Use a regular set when there are too many entries. */ | ||
| 42 | - if (len > server.set_max_intset_entries) { | ||
| 43 | + size_t max_entries = server.set_max_intset_entries; | ||
| 44 | + if (max_entries >= 1<<30) max_entries = 1<<30; | ||
| 45 | + if (len > max_entries) { | ||
| 46 | o = createSetObject(); | ||
| 47 | /* It's faster to expand the dict to the right size asap in order | ||
| 48 | * to avoid rehashing */ | ||
| 49 | diff --git a/src/t_set.c b/src/t_set.c | ||
| 50 | index b655b71..d50a05a 100644 | ||
| 51 | --- a/src/t_set.c | ||
| 52 | +++ b/src/t_set.c | ||
| 53 | @@ -66,7 +66,10 @@ int setTypeAdd(robj *subject, sds value) { | ||
| 54 | if (success) { | ||
| 55 | /* Convert to regular set when the intset contains | ||
| 56 | * too many entries. */ | ||
| 57 | - if (intsetLen(subject->ptr) > server.set_max_intset_entries) | ||
| 58 | + size_t max_entries = server.set_max_intset_entries; | ||
| 59 | + /* limit to 1G entries due to intset internals. */ | ||
| 60 | + if (max_entries >= 1<<30) max_entries = 1<<30; | ||
| 61 | + if (intsetLen(subject->ptr) > max_entries) | ||
| 62 | setTypeConvert(subject,OBJ_ENCODING_HT); | ||
| 63 | return 1; | ||
| 64 | } | ||
| 65 | -- | ||
| 66 | 2.17.1 | ||
| 67 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch deleted file mode 100644 index 14992b789a..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32761.patch +++ /dev/null | |||
| @@ -1,257 +0,0 @@ | |||
| 1 | From 835d15b5360e277e6f95529c4d8685946a977ddd Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Huang Zhw <huang_zhw@126.com> | ||
| 3 | Date: Wed, 21 Jul 2021 21:25:19 +0800 | ||
| 4 | Subject: [PATCH 1/1] On 32 bit platform, the bit position of | ||
| 5 | GETBIT/SETBIT/BITFIELD/BITCOUNT,BITPOS may overflow (see CVE-2021-32761) | ||
| 6 | (#9191) | ||
| 7 | |||
| 8 | GETBIT, SETBIT may access wrong address because of wrap. | ||
| 9 | BITCOUNT and BITPOS may return wrapped results. | ||
| 10 | BITFIELD may access the wrong address but also allocate insufficient memory and segfault (see CVE-2021-32761). | ||
| 11 | |||
| 12 | This commit uses `uint64_t` or `long long` instead of `size_t`. | ||
| 13 | related https://github.com/redis/redis/pull/8096 | ||
| 14 | |||
| 15 | At 32bit platform: | ||
| 16 | > setbit bit 4294967295 1 | ||
| 17 | (integer) 0 | ||
| 18 | > config set proto-max-bulk-len 536870913 | ||
| 19 | OK | ||
| 20 | > append bit "\xFF" | ||
| 21 | (integer) 536870913 | ||
| 22 | > getbit bit 4294967296 | ||
| 23 | (integer) 0 | ||
| 24 | |||
| 25 | When the bit index is larger than 4294967295, size_t can't hold bit index. In the past, `proto-max-bulk-len` is limit to 536870912, so there is no problem. | ||
| 26 | |||
| 27 | After this commit, bit position is stored in `uint64_t` or `long long`. So when `proto-max-bulk-len > 536870912`, 32bit platforms can still be correct. | ||
| 28 | |||
| 29 | For 64bit platform, this problem still exists. The major reason is bit pos 8 times of byte pos. When proto-max-bulk-len is very larger, bit pos may overflow. | ||
| 30 | But at 64bit platform, we don't have so long string. So this bug may never happen. | ||
| 31 | |||
| 32 | Additionally this commit add a test cost `512MB` memory which is tag as `large-memory`. Make freebsd ci and valgrind ci ignore this test. | ||
| 33 | |||
| 34 | (cherry picked from commit 71d452876ebf8456afaadd6b3c27988abadd1148)d | ||
| 35 | --- | ||
| 36 | |||
| 37 | CVE: CVE-2021-32761 | ||
| 38 | |||
| 39 | Upstream-Status: Backport [835d15b5360e277e6f95529c4d8685946a977ddd] | ||
| 40 | https://github.com/redis/redis.git | ||
| 41 | |||
| 42 | Signed-off-by: Joe Slater <joe.slater@windriver.com> | ||
| 43 | |||
| 44 | --- | ||
| 45 | .github/workflows/daily.yml | 6 +++--- | ||
| 46 | src/bitops.c | 32 ++++++++++++++++---------------- | ||
| 47 | src/server.h | 2 +- | ||
| 48 | tests/unit/bitops.tcl | 28 ++++++++++++++++++++++++++++ | ||
| 49 | 4 files changed, 48 insertions(+), 20 deletions(-) | ||
| 50 | |||
| 51 | diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml | ||
| 52 | index 9e4630e29..432971a9d 100644 | ||
| 53 | --- a/.github/workflows/daily.yml | ||
| 54 | +++ b/.github/workflows/daily.yml | ||
| 55 | @@ -151,7 +151,7 @@ jobs: | ||
| 56 | run: | | ||
| 57 | sudo apt-get update | ||
| 58 | sudo apt-get install tcl8.6 valgrind -y | ||
| 59 | - ./runtest --valgrind --verbose --clients 1 --dump-logs | ||
| 60 | + ./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs | ||
| 61 | - name: module api test | ||
| 62 | run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 | ||
| 63 | - name: unittest | ||
| 64 | @@ -171,7 +171,7 @@ jobs: | ||
| 65 | run: | | ||
| 66 | sudo apt-get update | ||
| 67 | sudo apt-get install tcl8.6 valgrind -y | ||
| 68 | - ./runtest --valgrind --verbose --clients 1 --dump-logs | ||
| 69 | + ./runtest --valgrind --verbose --clients 1 --tags -large-memory --dump-logs | ||
| 70 | - name: module api test | ||
| 71 | run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1 | ||
| 72 | |||
| 73 | @@ -260,7 +260,7 @@ jobs: | ||
| 74 | prepare: pkg install -y bash gmake lang/tcl86 | ||
| 75 | run: > | ||
| 76 | gmake && | ||
| 77 | - ./runtest --accurate --verbose --no-latency --dump-logs && | ||
| 78 | + ./runtest --accurate --verbose --no-latency --tags -large-memory --dump-logs && | ||
| 79 | MAKE=gmake ./runtest-moduleapi --verbose && | ||
| 80 | ./runtest-sentinel && | ||
| 81 | ./runtest-cluster | ||
| 82 | diff --git a/src/bitops.c b/src/bitops.c | ||
| 83 | index afd79ad88..f1c563a41 100644 | ||
| 84 | --- a/src/bitops.c | ||
| 85 | +++ b/src/bitops.c | ||
| 86 | @@ -37,8 +37,8 @@ | ||
| 87 | /* Count number of bits set in the binary array pointed by 's' and long | ||
| 88 | * 'count' bytes. The implementation of this function is required to | ||
| 89 | * work with an input string length up to 512 MB or more (server.proto_max_bulk_len) */ | ||
| 90 | -size_t redisPopcount(void *s, long count) { | ||
| 91 | - size_t bits = 0; | ||
| 92 | +long long redisPopcount(void *s, long count) { | ||
| 93 | + long long bits = 0; | ||
| 94 | unsigned char *p = s; | ||
| 95 | uint32_t *p4; | ||
| 96 | static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8}; | ||
| 97 | @@ -98,11 +98,11 @@ size_t redisPopcount(void *s, long count) { | ||
| 98 | * no zero bit is found, it returns count*8 assuming the string is zero | ||
| 99 | * padded on the right. However if 'bit' is 1 it is possible that there is | ||
| 100 | * not a single set bit in the bitmap. In this special case -1 is returned. */ | ||
| 101 | -long redisBitpos(void *s, unsigned long count, int bit) { | ||
| 102 | +long long redisBitpos(void *s, unsigned long count, int bit) { | ||
| 103 | unsigned long *l; | ||
| 104 | unsigned char *c; | ||
| 105 | unsigned long skipval, word = 0, one; | ||
| 106 | - long pos = 0; /* Position of bit, to return to the caller. */ | ||
| 107 | + long long pos = 0; /* Position of bit, to return to the caller. */ | ||
| 108 | unsigned long j; | ||
| 109 | int found; | ||
| 110 | |||
| 111 | @@ -410,7 +410,7 @@ void printBits(unsigned char *p, unsigned long count) { | ||
| 112 | * If the 'hash' argument is true, and 'bits is positive, then the command | ||
| 113 | * will also parse bit offsets prefixed by "#". In such a case the offset | ||
| 114 | * is multiplied by 'bits'. This is useful for the BITFIELD command. */ | ||
| 115 | -int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) { | ||
| 116 | +int getBitOffsetFromArgument(client *c, robj *o, uint64_t *offset, int hash, int bits) { | ||
| 117 | long long loffset; | ||
| 118 | char *err = "bit offset is not an integer or out of range"; | ||
| 119 | char *p = o->ptr; | ||
| 120 | @@ -435,7 +435,7 @@ int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int b | ||
| 121 | return C_ERR; | ||
| 122 | } | ||
| 123 | |||
| 124 | - *offset = (size_t)loffset; | ||
| 125 | + *offset = loffset; | ||
| 126 | return C_OK; | ||
| 127 | } | ||
| 128 | |||
| 129 | @@ -477,7 +477,7 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) { | ||
| 130 | * so that the 'maxbit' bit can be addressed. The object is finally | ||
| 131 | * returned. Otherwise if the key holds a wrong type NULL is returned and | ||
| 132 | * an error is sent to the client. */ | ||
| 133 | -robj *lookupStringForBitCommand(client *c, size_t maxbit) { | ||
| 134 | +robj *lookupStringForBitCommand(client *c, uint64_t maxbit) { | ||
| 135 | size_t byte = maxbit >> 3; | ||
| 136 | robj *o = lookupKeyWrite(c->db,c->argv[1]); | ||
| 137 | if (checkType(c,o,OBJ_STRING)) return NULL; | ||
| 138 | @@ -527,7 +527,7 @@ unsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) { | ||
| 139 | void setbitCommand(client *c) { | ||
| 140 | robj *o; | ||
| 141 | char *err = "bit is not an integer or out of range"; | ||
| 142 | - size_t bitoffset; | ||
| 143 | + uint64_t bitoffset; | ||
| 144 | ssize_t byte, bit; | ||
| 145 | int byteval, bitval; | ||
| 146 | long on; | ||
| 147 | @@ -566,7 +566,7 @@ void setbitCommand(client *c) { | ||
| 148 | void getbitCommand(client *c) { | ||
| 149 | robj *o; | ||
| 150 | char llbuf[32]; | ||
| 151 | - size_t bitoffset; | ||
| 152 | + uint64_t bitoffset; | ||
| 153 | size_t byte, bit; | ||
| 154 | size_t bitval = 0; | ||
| 155 | |||
| 156 | @@ -888,7 +888,7 @@ void bitposCommand(client *c) { | ||
| 157 | addReplyLongLong(c, -1); | ||
| 158 | } else { | ||
| 159 | long bytes = end-start+1; | ||
| 160 | - long pos = redisBitpos(p+start,bytes,bit); | ||
| 161 | + long long pos = redisBitpos(p+start,bytes,bit); | ||
| 162 | |||
| 163 | /* If we are looking for clear bits, and the user specified an exact | ||
| 164 | * range with start-end, we can't consider the right of the range as | ||
| 165 | @@ -897,11 +897,11 @@ void bitposCommand(client *c) { | ||
| 166 | * So if redisBitpos() returns the first bit outside the range, | ||
| 167 | * we return -1 to the caller, to mean, in the specified range there | ||
| 168 | * is not a single "0" bit. */ | ||
| 169 | - if (end_given && bit == 0 && pos == bytes*8) { | ||
| 170 | + if (end_given && bit == 0 && pos == (long long)bytes<<3) { | ||
| 171 | addReplyLongLong(c,-1); | ||
| 172 | return; | ||
| 173 | } | ||
| 174 | - if (pos != -1) pos += start*8; /* Adjust for the bytes we skipped. */ | ||
| 175 | + if (pos != -1) pos += (long long)start<<3; /* Adjust for the bytes we skipped. */ | ||
| 176 | addReplyLongLong(c,pos); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | @@ -933,12 +933,12 @@ struct bitfieldOp { | ||
| 180 | * GET subcommand is allowed, other subcommands will return an error. */ | ||
| 181 | void bitfieldGeneric(client *c, int flags) { | ||
| 182 | robj *o; | ||
| 183 | - size_t bitoffset; | ||
| 184 | + uint64_t bitoffset; | ||
| 185 | int j, numops = 0, changes = 0; | ||
| 186 | struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */ | ||
| 187 | int owtype = BFOVERFLOW_WRAP; /* Overflow type. */ | ||
| 188 | int readonly = 1; | ||
| 189 | - size_t highest_write_offset = 0; | ||
| 190 | + uint64_t highest_write_offset = 0; | ||
| 191 | |||
| 192 | for (j = 2; j < c->argc; j++) { | ||
| 193 | int remargs = c->argc-j-1; /* Remaining args other than current. */ | ||
| 194 | @@ -1128,9 +1128,9 @@ void bitfieldGeneric(client *c, int flags) { | ||
| 195 | * object boundaries. */ | ||
| 196 | memset(buf,0,9); | ||
| 197 | int i; | ||
| 198 | - size_t byte = thisop->offset >> 3; | ||
| 199 | + uint64_t byte = thisop->offset >> 3; | ||
| 200 | for (i = 0; i < 9; i++) { | ||
| 201 | - if (src == NULL || i+byte >= (size_t)strlen) break; | ||
| 202 | + if (src == NULL || i+byte >= (uint64_t)strlen) break; | ||
| 203 | buf[i] = src[i+byte]; | ||
| 204 | } | ||
| 205 | |||
| 206 | diff --git a/src/server.h b/src/server.h | ||
| 207 | index 67541fe60..caf9df31c 100644 | ||
| 208 | --- a/src/server.h | ||
| 209 | +++ b/src/server.h | ||
| 210 | @@ -1795,7 +1795,7 @@ void getRandomHexChars(char *p, size_t len); | ||
| 211 | void getRandomBytes(unsigned char *p, size_t len); | ||
| 212 | uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l); | ||
| 213 | void exitFromChild(int retcode); | ||
| 214 | -size_t redisPopcount(void *s, long count); | ||
| 215 | +long long redisPopcount(void *s, long count); | ||
| 216 | int redisSetProcTitle(char *title); | ||
| 217 | int validateProcTitleTemplate(const char *template); | ||
| 218 | int redisCommunicateSystemd(const char *sd_notify_msg); | ||
| 219 | diff --git a/tests/unit/bitops.tcl b/tests/unit/bitops.tcl | ||
| 220 | index 926f38295..534832974 100644 | ||
| 221 | --- a/tests/unit/bitops.tcl | ||
| 222 | +++ b/tests/unit/bitops.tcl | ||
| 223 | @@ -349,3 +349,31 @@ start_server {tags {"bitops"}} { | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | + | ||
| 228 | +start_server {tags {"bitops large-memory"}} { | ||
| 229 | + test "BIT pos larger than UINT_MAX" { | ||
| 230 | + set bytes [expr (1 << 29) + 1] | ||
| 231 | + set bitpos [expr (1 << 32)] | ||
| 232 | + set oldval [lindex [r config get proto-max-bulk-len] 1] | ||
| 233 | + r config set proto-max-bulk-len $bytes | ||
| 234 | + r setbit mykey $bitpos 1 | ||
| 235 | + assert_equal $bytes [r strlen mykey] | ||
| 236 | + assert_equal 1 [r getbit mykey $bitpos] | ||
| 237 | + assert_equal [list 128 128 -1] [r bitfield mykey get u8 $bitpos set u8 $bitpos 255 get i8 $bitpos] | ||
| 238 | + assert_equal $bitpos [r bitpos mykey 1] | ||
| 239 | + assert_equal $bitpos [r bitpos mykey 1 [expr $bytes - 1]] | ||
| 240 | + if {$::accurate} { | ||
| 241 | + # set all bits to 1 | ||
| 242 | + set mega [expr (1 << 23)] | ||
| 243 | + set part [string repeat "\xFF" $mega] | ||
| 244 | + for {set i 0} {$i < 64} {incr i} { | ||
| 245 | + r setrange mykey [expr $i * $mega] $part | ||
| 246 | + } | ||
| 247 | + r setrange mykey [expr $bytes - 1] "\xFF" | ||
| 248 | + assert_equal [expr $bitpos + 8] [r bitcount mykey] | ||
| 249 | + assert_equal -1 [r bitpos mykey 0 0 [expr $bytes - 1]] | ||
| 250 | + } | ||
| 251 | + r config set proto-max-bulk-len $oldval | ||
| 252 | + r del mykey | ||
| 253 | + } {1} | ||
| 254 | +} | ||
| 255 | -- | ||
| 256 | 2.24.1 | ||
| 257 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-32762.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-32762.patch deleted file mode 100644 index ec6e2fbd5b..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-32762.patch +++ /dev/null | |||
| @@ -1,68 +0,0 @@ | |||
| 1 | From 4b1de5438ad9ef2236c379f2f78feb9f1fd9796e Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Mon, 4 Oct 2021 12:10:17 +0300 | ||
| 4 | Subject: [PATCH] Fix redis-cli / redis-sential overflow on some platforms | ||
| 5 | (CVE-2021-32762) (#9587) | ||
| 6 | |||
| 7 | The redis-cli command line tool and redis-sentinel service may be vulnerable | ||
| 8 | to integer overflow when parsing specially crafted large multi-bulk network | ||
| 9 | replies. This is a result of a vulnerability in the underlying hiredis | ||
| 10 | library which does not perform an overflow check before calling the calloc() | ||
| 11 | heap allocation function. | ||
| 12 | |||
| 13 | This issue only impacts systems with heap allocators that do not perform their | ||
| 14 | own overflow checks. Most modern systems do and are therefore not likely to | ||
| 15 | be affected. Furthermore, by default redis-sentinel uses the jemalloc allocator | ||
| 16 | which is also not vulnerable. | ||
| 17 | |||
| 18 | Co-authored-by: Yossi Gottlieb <yossigo@gmail.com> | ||
| 19 | |||
| 20 | CVE: CVE-2021-32762 | ||
| 21 | Upstream-Status: Backport[https://github.com/redis/redis/commit/0215324a66af949be39b34be2d55143232c1cb71] | ||
| 22 | |||
| 23 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 24 | --- | ||
| 25 | deps/hiredis/hiredis.c | 1 + | ||
| 26 | deps/hiredis/test.c | 14 ++++++++++++++ | ||
| 27 | 2 files changed, 15 insertions(+) | ||
| 28 | |||
| 29 | diff --git a/deps/hiredis/hiredis.c b/deps/hiredis/hiredis.c | ||
| 30 | index 51f22a6..990f619 100644 | ||
| 31 | --- a/deps/hiredis/hiredis.c | ||
| 32 | +++ b/deps/hiredis/hiredis.c | ||
| 33 | @@ -174,6 +174,7 @@ static void *createArrayObject(const redisReadTask *task, size_t elements) { | ||
| 34 | return NULL; | ||
| 35 | |||
| 36 | if (elements > 0) { | ||
| 37 | + if (SIZE_MAX / sizeof(redisReply*) < elements) return NULL; /* Don't overflow */ | ||
| 38 | r->element = hi_calloc(elements,sizeof(redisReply*)); | ||
| 39 | if (r->element == NULL) { | ||
| 40 | freeReplyObject(r); | ||
| 41 | diff --git a/deps/hiredis/test.c b/deps/hiredis/test.c | ||
| 42 | index 8295367..bdff74e 100644 | ||
| 43 | --- a/deps/hiredis/test.c | ||
| 44 | +++ b/deps/hiredis/test.c | ||
| 45 | @@ -498,6 +498,20 @@ static void test_reply_reader(void) { | ||
| 46 | freeReplyObject(reply); | ||
| 47 | redisReaderFree(reader); | ||
| 48 | |||
| 49 | + test("Multi-bulk never overflows regardless of maxelements: "); | ||
| 50 | + size_t bad_mbulk_len = (SIZE_MAX / sizeof(void *)) + 3; | ||
| 51 | + char bad_mbulk_reply[100]; | ||
| 52 | + snprintf(bad_mbulk_reply, sizeof(bad_mbulk_reply), "*%llu\r\n+asdf\r\n", | ||
| 53 | + (unsigned long long) bad_mbulk_len); | ||
| 54 | + | ||
| 55 | + reader = redisReaderCreate(); | ||
| 56 | + reader->maxelements = 0; /* Don't rely on default limit */ | ||
| 57 | + redisReaderFeed(reader, bad_mbulk_reply, strlen(bad_mbulk_reply)); | ||
| 58 | + ret = redisReaderGetReply(reader,&reply); | ||
| 59 | + test_cond(ret == REDIS_ERR && strcasecmp(reader->errstr, "Out of memory") == 0); | ||
| 60 | + freeReplyObject(reply); | ||
| 61 | + redisReaderFree(reader); | ||
| 62 | + | ||
| 63 | #if LLONG_MAX > SIZE_MAX | ||
| 64 | test("Set error when array > SIZE_MAX: "); | ||
| 65 | reader = redisReaderCreate(); | ||
| 66 | -- | ||
| 67 | 2.17.1 | ||
| 68 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/CVE-2021-41099.patch b/meta-oe/recipes-extended/redis/redis/CVE-2021-41099.patch deleted file mode 100644 index ce0e112aeb..0000000000 --- a/meta-oe/recipes-extended/redis/redis/CVE-2021-41099.patch +++ /dev/null | |||
| @@ -1,47 +0,0 @@ | |||
| 1 | From fd25ce2108994b7781269143bdfb3403faa2f1d1 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: YiyuanGUO <yguoaz@gmail.com> | ||
| 3 | Date: Wed, 29 Sep 2021 10:20:35 +0300 | ||
| 4 | Subject: [PATCH] Fix integer overflow in _sdsMakeRoomFor (CVE-2021-41099) | ||
| 5 | |||
| 6 | CVE: CVE-2021-41099 | ||
| 7 | Upstream-Status: Backport[https://github.com/redis/redis/commit/c6ad876774f3cc11e32681ea02a2eead00f2c521] | ||
| 8 | |||
| 9 | Signed-off-by: Changqing Li <changqing.li@windriver.com> | ||
| 10 | --- | ||
| 11 | src/sds.c | 6 +++--- | ||
| 12 | 1 file changed, 3 insertions(+), 3 deletions(-) | ||
| 13 | |||
| 14 | diff --git a/src/sds.c b/src/sds.c | ||
| 15 | index 2ec3aa7..5eadae5 100644 | ||
| 16 | --- a/src/sds.c | ||
| 17 | +++ b/src/sds.c | ||
| 18 | @@ -233,7 +233,7 @@ void sdsclear(sds s) { | ||
| 19 | sds sdsMakeRoomFor(sds s, size_t addlen) { | ||
| 20 | void *sh, *newsh; | ||
| 21 | size_t avail = sdsavail(s); | ||
| 22 | - size_t len, newlen; | ||
| 23 | + size_t len, newlen, reqlen; | ||
| 24 | char type, oldtype = s[-1] & SDS_TYPE_MASK; | ||
| 25 | int hdrlen; | ||
| 26 | size_t usable; | ||
| 27 | @@ -243,7 +243,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { | ||
| 28 | |||
| 29 | len = sdslen(s); | ||
| 30 | sh = (char*)s-sdsHdrSize(oldtype); | ||
| 31 | - newlen = (len+addlen); | ||
| 32 | + reqlen = newlen = (len+addlen); | ||
| 33 | assert(newlen > len); /* Catch size_t overflow */ | ||
| 34 | if (newlen < SDS_MAX_PREALLOC) | ||
| 35 | newlen *= 2; | ||
| 36 | @@ -258,7 +258,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { | ||
| 37 | if (type == SDS_TYPE_5) type = SDS_TYPE_8; | ||
| 38 | |||
| 39 | hdrlen = sdsHdrSize(type); | ||
| 40 | - assert(hdrlen + newlen + 1 > len); /* Catch size_t overflow */ | ||
| 41 | + assert(hdrlen + newlen + 1 > reqlen); /* Catch size_t overflow */ | ||
| 42 | if (oldtype==type) { | ||
| 43 | newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable); | ||
| 44 | if (newsh == NULL) return NULL; | ||
| 45 | -- | ||
| 46 | 2.17.1 | ||
| 47 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29477.patch b/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29477.patch deleted file mode 100644 index a5e5a1ba55..0000000000 --- a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29477.patch +++ /dev/null | |||
| @@ -1,35 +0,0 @@ | |||
| 1 | From f0c5f920d0f88bd8aa376a2c05af4902789d1ef9 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Mon, 3 May 2021 08:32:31 +0300 | ||
| 4 | Subject: [PATCH] Fix integer overflow in STRALGO LCS (CVE-2021-29477) | ||
| 5 | |||
| 6 | An integer overflow bug in Redis version 6.0 or newer could be exploited using | ||
| 7 | the STRALGO LCS command to corrupt the heap and potentially result with remote | ||
| 8 | code execution. | ||
| 9 | |||
| 10 | CVE: CVE-2021-29477 | ||
| 11 | Upstream-Status: Backport | ||
| 12 | [https://github.com/redis/redis/commit/f0c5f920d0f88bd8aa376a2c05af4902789d1ef9] | ||
| 13 | |||
| 14 | Signed-off-by: Tony Tascioglu <tony.tascioglu@windriver.com> | ||
| 15 | |||
| 16 | --- | ||
| 17 | src/t_string.c | 2 +- | ||
| 18 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 19 | |||
| 20 | diff --git a/src/t_string.c b/src/t_string.c | ||
| 21 | index 9228c5ed0..db6f7042e 100644 | ||
| 22 | --- a/src/t_string.c | ||
| 23 | +++ b/src/t_string.c | ||
| 24 | @@ -805,7 +805,7 @@ void stralgoLCS(client *c) { | ||
| 25 | /* Setup an uint32_t array to store at LCS[i,j] the length of the | ||
| 26 | * LCS A0..i-1, B0..j-1. Note that we have a linear array here, so | ||
| 27 | * we index it as LCS[j+(blen+1)*j] */ | ||
| 28 | - uint32_t *lcs = zmalloc((alen+1)*(blen+1)*sizeof(uint32_t)); | ||
| 29 | + uint32_t *lcs = zmalloc((size_t)(alen+1)*(blen+1)*sizeof(uint32_t)); | ||
| 30 | #define LCS(A,B) lcs[(B)+((A)*(blen+1))] | ||
| 31 | |||
| 32 | /* Start building the LCS table. */ | ||
| 33 | -- | ||
| 34 | 2.32.0 | ||
| 35 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29478.patch b/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29478.patch deleted file mode 100644 index ebbf6e1b94..0000000000 --- a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-29478.patch +++ /dev/null | |||
| @@ -1,42 +0,0 @@ | |||
| 1 | From 29900d4e6bccdf3691bedf0ea9a5d84863fa3592 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Mon, 3 May 2021 08:27:22 +0300 | ||
| 4 | Subject: [PATCH] Fix integer overflow in intset (CVE-2021-29478) | ||
| 5 | |||
| 6 | An integer overflow bug in Redis 6.2 could be exploited to corrupt the heap and | ||
| 7 | potentially result with remote code execution. | ||
| 8 | |||
| 9 | The vulnerability involves changing the default set-max-intset-entries | ||
| 10 | configuration value, creating a large set key that consists of integer values | ||
| 11 | and using the COPY command to duplicate it. | ||
| 12 | |||
| 13 | The integer overflow bug exists in all versions of Redis starting with 2.6, | ||
| 14 | where it could result with a corrupted RDB or DUMP payload, but not exploited | ||
| 15 | through COPY (which did not exist before 6.2). | ||
| 16 | |||
| 17 | CVE: CVE-2021-29478 | ||
| 18 | Upstream-Status: Backport | ||
| 19 | [https://github.com/redis/redis/commit/29900d4e6bccdf3691bedf0ea9a5d84863fa3592] | ||
| 20 | |||
| 21 | Signed-off-by: Tony Tascioglu <tony.tascioglu@windriver.com> | ||
| 22 | |||
| 23 | --- | ||
| 24 | src/intset.c | 2 +- | ||
| 25 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 26 | |||
| 27 | diff --git a/src/intset.c b/src/intset.c | ||
| 28 | index 1a64ecae8..9ba13898d 100644 | ||
| 29 | --- a/src/intset.c | ||
| 30 | +++ b/src/intset.c | ||
| 31 | @@ -281,7 +281,7 @@ uint32_t intsetLen(const intset *is) { | ||
| 32 | |||
| 33 | /* Return intset blob size in bytes. */ | ||
| 34 | size_t intsetBlobLen(intset *is) { | ||
| 35 | - return sizeof(intset)+intrev32ifbe(is->length)*intrev32ifbe(is->encoding); | ||
| 36 | + return sizeof(intset)+(size_t)intrev32ifbe(is->length)*intrev32ifbe(is->encoding); | ||
| 37 | } | ||
| 38 | |||
| 39 | /* Validate the integrity of the data structure. | ||
| 40 | -- | ||
| 41 | 2.32.0 | ||
| 42 | |||
diff --git a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-32625.patch b/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-32625.patch deleted file mode 100644 index 6311a5db10..0000000000 --- a/meta-oe/recipes-extended/redis/redis/fix-CVE-2021-32625.patch +++ /dev/null | |||
| @@ -1,61 +0,0 @@ | |||
| 1 | From e9a1438ac4c52aa68dfa2a8324b6419356842116 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Oran Agra <oran@redislabs.com> | ||
| 3 | Date: Tue, 1 Jun 2021 09:12:45 +0300 | ||
| 4 | Subject: [PATCH] Fix integer overflow in STRALGO LCS (CVE-2021-32625) (#9011) | ||
| 5 | |||
| 6 | An integer overflow bug in Redis version 6.0 or newer can be exploited using the | ||
| 7 | STRALGO LCS command to corrupt the heap and potentially result with remote code | ||
| 8 | execution. This is a result of an incomplete fix by CVE-2021-29477. | ||
| 9 | |||
| 10 | (cherry picked from commit 1ddecf1958924b178b76a31d989ef1e05af81964) | ||
| 11 | |||
| 12 | |||
| 13 | CVE: CVE-2021-32625 | ||
| 14 | Upstream-Status: Backport [e9a1438ac4c52aa68dfa2a8324b6419356842116] | ||
| 15 | |||
| 16 | Signed-off-by: Tony Tascioglu <tony.tascioglu@windriver.com> | ||
| 17 | --- | ||
| 18 | src/t_string.c | 18 +++++++++++++++++- | ||
| 19 | 1 file changed, 17 insertions(+), 1 deletion(-) | ||
| 20 | |||
| 21 | diff --git a/src/t_string.c b/src/t_string.c | ||
| 22 | index 490d5983a..587d3aeb8 100644 | ||
| 23 | --- a/src/t_string.c | ||
| 24 | +++ b/src/t_string.c | ||
| 25 | @@ -797,6 +797,12 @@ void stralgoLCS(client *c) { | ||
| 26 | goto cleanup; | ||
| 27 | } | ||
| 28 | |||
| 29 | + /* Detect string truncation or later overflows. */ | ||
| 30 | + if (sdslen(a) >= UINT32_MAX-1 || sdslen(b) >= UINT32_MAX-1) { | ||
| 31 | + addReplyError(c, "String too long for LCS"); | ||
| 32 | + goto cleanup; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | /* Compute the LCS using the vanilla dynamic programming technique of | ||
| 36 | * building a table of LCS(x,y) substrings. */ | ||
| 37 | uint32_t alen = sdslen(a); | ||
| 38 | @@ -805,9 +811,19 @@ void stralgoLCS(client *c) { | ||
| 39 | /* Setup an uint32_t array to store at LCS[i,j] the length of the | ||
| 40 | * LCS A0..i-1, B0..j-1. Note that we have a linear array here, so | ||
| 41 | * we index it as LCS[j+(blen+1)*j] */ | ||
| 42 | - uint32_t *lcs = zmalloc((size_t)(alen+1)*(blen+1)*sizeof(uint32_t)); | ||
| 43 | #define LCS(A,B) lcs[(B)+((A)*(blen+1))] | ||
| 44 | |||
| 45 | + /* Try to allocate the LCS table, and abort on overflow or insufficient memory. */ | ||
| 46 | + unsigned long long lcssize = (unsigned long long)(alen+1)*(blen+1); /* Can't overflow due to the size limits above. */ | ||
| 47 | + unsigned long long lcsalloc = lcssize * sizeof(uint32_t); | ||
| 48 | + uint32_t *lcs = NULL; | ||
| 49 | + if (lcsalloc < SIZE_MAX && lcsalloc / lcssize == sizeof(uint32_t)) | ||
| 50 | + lcs = ztrymalloc(lcsalloc); | ||
| 51 | + if (!lcs) { | ||
| 52 | + addReplyError(c, "Insufficient memory"); | ||
| 53 | + goto cleanup; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | /* Start building the LCS table. */ | ||
| 57 | for (uint32_t i = 0; i <= alen; i++) { | ||
| 58 | for (uint32_t j = 0; j <= blen; j++) { | ||
| 59 | -- | ||
| 60 | 2.32.0 | ||
| 61 | |||
diff --git a/meta-oe/recipes-extended/redis/redis_6.2.2.bb b/meta-oe/recipes-extended/redis/redis_6.2.6.bb index 4317c10605..c129e61988 100644 --- a/meta-oe/recipes-extended/redis/redis_6.2.2.bb +++ b/meta-oe/recipes-extended/redis/redis_6.2.6.bb | |||
| @@ -13,21 +13,8 @@ SRC_URI = "http://download.redis.io/releases/${BP}.tar.gz \ | |||
| 13 | file://hiredis-use-default-CC-if-it-is-set.patch \ | 13 | file://hiredis-use-default-CC-if-it-is-set.patch \ |
| 14 | file://lua-update-Makefile-to-use-environment-build-setting.patch \ | 14 | file://lua-update-Makefile-to-use-environment-build-setting.patch \ |
| 15 | file://oe-use-libc-malloc.patch \ | 15 | file://oe-use-libc-malloc.patch \ |
| 16 | file://0001-src-Do-not-reset-FINAL_LIBS.patch \ | ||
| 17 | file://GNU_SOURCE.patch \ | ||
| 18 | file://0006-Define-correct-gregs-for-RISCV32.patch \ | ||
| 19 | file://fix-CVE-2021-29477.patch \ | ||
| 20 | file://fix-CVE-2021-29478.patch \ | ||
| 21 | file://fix-CVE-2021-32625.patch \ | ||
| 22 | file://CVE-2021-32761.patch \ | ||
| 23 | file://CVE-2021-41099.patch \ | ||
| 24 | file://CVE-2021-32762.patch \ | ||
| 25 | file://CVE-2021-32687.patch \ | ||
| 26 | file://CVE-2021-32675.patch \ | ||
| 27 | file://CVE-2021-32627-CVE-2021-32628.patch \ | ||
| 28 | file://CVE-2021-32626.patch \ | ||
| 29 | " | 16 | " |
| 30 | SRC_URI[sha256sum] = "7a260bb74860f1b88c3d5942bf8ba60ca59f121c6dce42d3017bed6add0b9535" | 17 | SRC_URI[sha256sum] = "5b2b8b7a50111ef395bf1c1d5be11e6e167ac018125055daa8b5c2317ae131ab" |
| 31 | 18 | ||
| 32 | inherit autotools-brokensep update-rc.d systemd useradd | 19 | inherit autotools-brokensep update-rc.d systemd useradd |
| 33 | 20 | ||
