diff --git a/CVE-2021-32626.patch b/CVE-2021-32626.patch new file mode 100644 index 0000000000000000000000000000000000000000..44c3cb4cc1927244d2c059abee6106d6a20cfd24 --- /dev/null +++ b/CVE-2021-32626.patch @@ -0,0 +1,140 @@ +From 666ed7facf4524bf6d19b11b20faa2cf93fdf591 Mon Sep 17 00:00:00 2001 +From: "meir@redislabs.com" +Date: Sun, 13 Jun 2021 14:27:18 +0300 +Subject: [PATCH] Fix invalid memory write on lua stack overflow + {CVE-2021-32626} +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When LUA call our C code, by default, the LUA stack has room for 20 +elements. In most cases, this is more than enough but sometimes it's not +and the caller must verify the LUA stack size before he pushes elements. + +On 3 places in the code, there was no verification of the LUA stack size. +On specific inputs this missing verification could have lead to invalid +memory write: +1. On 'luaReplyToRedisReply', one might return a nested reply that will + explode the LUA stack. +2. On 'redisProtocolToLuaType', the Redis reply might be deep enough +   to explode the LUA stack (notice that currently there is no such +   command in Redis that returns such a nested reply, but modules might +   do it) +3. On 'ldbRedis', one might give a command with enough arguments to +   explode the LUA stack (all the arguments will be pushed to the LUA +   stack) + +This commit is solving all those 3 issues by calling 'lua_checkstack' and +verify that there is enough room in the LUA stack to push elements. In +case 'lua_checkstack' returns an error (there is not enough room in the +LUA stack and it's not possible to increase the stack), we will do the +following: +1. On 'luaReplyToRedisReply', we will return an error to the user. +2. On 'redisProtocolToLuaType' we will exit with panic (we assume this + scenario is rare because it can only happen with a module). +3. On 'ldbRedis', we return an error. +--- + src/scripting.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + +diff --git a/src/scripting.c b/src/scripting.c +index dea5f516561e..afa6adb0c47e 100644 +--- a/src/scripting.c ++++ b/src/scripting.c +@@ -128,6 +128,16 @@ void sha1hex(char *digest, char *script, size_t len) { + */ + + char *redisProtocolToLuaType(lua_State *lua, char* reply) { ++ ++ if (!lua_checkstack(lua, 5)) { ++ /* ++ * Increase the Lua stack if needed, to make sure there is enough room ++ * to push 5 elements to the stack. On failure, exit with panic. ++         * Notice that we need, in the worst case, 5 elements because redisProtocolToLuaType_Aggregate ++         * might push 5 elements to the Lua stack.*/ ++ serverPanic("lua stack limit reach when parsing redis.call reply"); ++ } ++ + char *p = reply; + + switch(*p) { +@@ -220,6 +230,11 @@ char *redisProtocolToLuaType_Aggregate(lua_State *lua, char *reply, int atype) { + if (atype == '%') { + p = redisProtocolToLuaType(lua,p); + } else { ++ if (!lua_checkstack(lua, 1)) { ++ /* Notice that here we need to check the stack again because the recursive ++ * call to redisProtocolToLuaType might have use the room allocated in the stack */ ++ serverPanic("lua stack limit reach when parsing redis.call reply"); ++ } + lua_pushboolean(lua,1); + } + lua_settable(lua,-3); +@@ -339,6 +354,17 @@ void luaSortArray(lua_State *lua) { + /* Reply to client 'c' converting the top element in the Lua stack to a + * Redis reply. As a side effect the element is consumed from the stack. */ + void luaReplyToRedisReply(client *c, lua_State *lua) { ++ ++ if (!lua_checkstack(lua, 4)) { ++ /* Increase the Lua stack if needed to make sure there is enough room ++ * to push 4 elements to the stack. On failure, return error. ++         * Notice that we need, in the worst case, 4 elements because returning a map might ++ * require push 4 elements to the Lua stack.*/ ++ addReplyErrorFormat(c, "reached lua stack limit"); ++ lua_pop(lua,1); // pop the element from the stack ++ return; ++ } ++ + int t = lua_type(lua,-1); + + switch(t) { +@@ -362,6 +388,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { + * field. */ + + /* Handle error reply. */ ++ // we took care of the stack size on function start + lua_pushstring(lua,"err"); + lua_gettable(lua,-2); + t = lua_type(lua,-1); +@@ -407,6 +434,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { + if (t == LUA_TTABLE) { + int maplen = 0; + void *replylen = addReplyDeferredLen(c); ++ /* we took care of the stack size on function start */ + lua_pushnil(lua); /* Use nil to start iteration. */ + while (lua_next(lua,-2)) { + /* Stack now: table, key, value */ +@@ -429,6 +457,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { + if (t == LUA_TTABLE) { + int setlen = 0; + void *replylen = addReplyDeferredLen(c); ++ /* we took care of the stack size on function start */ + lua_pushnil(lua); /* Use nil to start iteration. */ + while (lua_next(lua,-2)) { + /* Stack now: table, key, true */ +@@ -448,6 +477,7 @@ void luaReplyToRedisReply(client *c, lua_State *lua) { + void *replylen = addReplyDeferredLen(c); + int j = 1, mbulklen = 0; + while(1) { ++ /* we took care of the stack size on function start */ + lua_pushnumber(lua,j++); + lua_gettable(lua,-2); + t = lua_type(lua,-1); +@@ -2506,6 +2536,17 @@ void ldbEval(lua_State *lua, sds *argv, int argc) { + void ldbRedis(lua_State *lua, sds *argv, int argc) { + int j, saved_rc = server.lua_replicate_commands; + ++ if (!lua_checkstack(lua, argc + 1)) { ++ /* Increase the Lua stack if needed to make sure there is enough room ++ * to push 'argc + 1' elements to the stack. On failure, return error. ++         * Notice that we need, in worst case, 'argc + 1' elements because we push all the arguments ++         * given by the user (without the first argument) and we also push the 'redis' global table and ++         * 'redis.call' function so: ++         * (1 (redis table)) + (1 (redis.call function)) + (argc - 1 (all arguments without the first)) = argc + 1*/ ++ ldbLogRedisReply("max lua stack reached"); ++ return; ++ } ++ + lua_getglobal(lua,"redis"); + lua_pushstring(lua,"call"); + lua_gettable(lua,-2); /* Stack: redis, redis.call */ diff --git a/redis6.spec b/redis6.spec index 1135107d020293278a70d2bfeeaba62e2f52ec19..fd86354f086679284c8610b5f2f76c3704d6ccf1 100644 --- a/redis6.spec +++ b/redis6.spec @@ -6,7 +6,7 @@ %global Pname redis Name: redis6 Version: 6.0.11 -Release: 3 +Release: 4 Summary: A persistent key-value database License: BSD and MIT URL: https://redis.io @@ -21,6 +21,7 @@ Source10: https://github.com/%{Pname}/%{Pname}-doc/archive/%{doc_comm Patch0001: Modify-aarch64-architecture-jemalloc-page-size-from-from-4k-to-64k.patch +Patch0002: CVE-2021-32626.patch BuildRequires: make gcc %if %{with tests} BuildRequires: procps-ng tcl @@ -79,6 +80,7 @@ tar -xvf %{SOURCE10} %setup -n %{Pname}-%{version} %ifarch aarch64 %patch0001 -p1 +%patch0002 -p1 %endif mv ../%{Pname}-doc-%{doc_commit} doc mv deps/lua/COPYRIGHT COPYRIGHT-lua @@ -208,6 +210,9 @@ fi %{_docdir}/%{Pname} %changelog +* Thu Nov 04 2021 liwu - 6.0.11-4 +- Fix CVE-2021-32626 + * Thu Sep 02 2021 lingsheng - 6.0.11-3 - Fix missing patch in source package