diff --git a/backport-CVE-2025-21605.patch b/backport-CVE-2025-21605.patch new file mode 100644 index 0000000000000000000000000000000000000000..44d1fec5eaae51f77781822c58f4edea4c87ebff --- /dev/null +++ b/backport-CVE-2025-21605.patch @@ -0,0 +1,61 @@ +From 5e93f9cb9dbc3e7ac9bce36f2838156cbc5c9e62 Mon Sep 17 00:00:00 2001 +From: YaacovHazan +Date: Wed, 23 Apr 2025 08:09:40 +0000 +Subject: [PATCH] Limiting output buffer for unauthenticated client + (CVE-2025-21605) + +For unauthenticated clients the output buffer is limited to prevent +them from abusing it by not reading the replies +--- + src/networking.c | 5 +++++ + tests/unit/auth.tcl | 18 ++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/src/networking.c b/src/networking.c +index 9f654063c..11891d3e9 100644 +--- a/src/networking.c ++++ b/src/networking.c +@@ -3285,6 +3285,11 @@ int checkClientOutputBufferLimits(client *c) { + int soft = 0, hard = 0, class; + unsigned long used_mem = getClientOutputBufferMemoryUsage(c); + ++ /* For unauthenticated clients the output buffer is limited to prevent ++ * them from abusing it by not reading the replies */ ++ if (used_mem > 1024 && authRequired(c)) ++ return 1; ++ + class = getClientType(c); + /* For the purpose of output buffer limiting, masters are handled + * like normal clients. */ +diff --git a/tests/unit/auth.tcl b/tests/unit/auth.tcl +index 5997707c6..2a730d766 100644 +--- a/tests/unit/auth.tcl ++++ b/tests/unit/auth.tcl +@@ -40,6 +40,24 @@ start_server {tags {"auth"} overrides {requirepass foobar}} { + assert_match {*unauthenticated bulk length*} $e + $rr close + } ++ ++ test {For unauthenticated clients output buffer is limited} { ++ set rr [redis [srv "host"] [srv "port"] 1 $::tls] ++ $rr SET x 5 ++ catch {[$rr read]} e ++ assert_match {*NOAUTH Authentication required*} $e ++ ++ # Fill the output buffer in a loop without reading it and make ++ # sure the client disconnected. ++ # Considering the socket eat some of the replies, we are testing ++ # that such client can't consume more than few MB's. ++ catch { ++ for {set j 0} {$j < 1000000} {incr j} { ++ $rr SET x 5 ++ } ++ } e ++ assert_match {I/O error reading reply} $e ++ } + } + + start_server {tags {"auth_binary_password"}} { +-- +2.51.0 + diff --git a/backport-CVE-2025-32023.patch b/backport-CVE-2025-32023.patch new file mode 100644 index 0000000000000000000000000000000000000000..21bc4b86cfd9b7ab604dd83af4d9b25574cbe7e3 --- /dev/null +++ b/backport-CVE-2025-32023.patch @@ -0,0 +1,214 @@ +From df47cffd065fc886a76460959a6e2205117d0d30 Mon Sep 17 00:00:00 2001 +From: "debing.sun" +Date: Wed, 7 May 2025 18:25:06 +0800 +Subject: [PATCH] Fix out of bounds write in hyperloglog commands + (CVE-2025-32023) + +Co-authored-by: oranagra +--- + src/hyperloglog.c | 47 +++++++++++++++++++++++++++++++---- + tests/unit/hyperloglog.tcl | 51 ++++++++++++++++++++++++++++++++++++++ + 2 files changed, 93 insertions(+), 5 deletions(-) + +diff --git a/src/hyperloglog.c b/src/hyperloglog.c +index 75a04227c..7cabfa13e 100644 +--- a/src/hyperloglog.c ++++ b/src/hyperloglog.c +@@ -586,6 +586,7 @@ int hllSparseToDense(robj *o) { + struct hllhdr *hdr, *oldhdr = (struct hllhdr*)sparse; + int idx = 0, runlen, regval; + uint8_t *p = (uint8_t*)sparse, *end = p+sdslen(sparse); ++ int valid = 1; + + /* If the representation is already the right one return ASAP. */ + hdr = (struct hllhdr*) sparse; +@@ -605,16 +606,27 @@ int hllSparseToDense(robj *o) { + while(p < end) { + if (HLL_SPARSE_IS_ZERO(p)) { + runlen = HLL_SPARSE_ZERO_LEN(p); ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + idx += runlen; + p++; + } else if (HLL_SPARSE_IS_XZERO(p)) { + runlen = HLL_SPARSE_XZERO_LEN(p); ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + idx += runlen; + p += 2; + } else { + runlen = HLL_SPARSE_VAL_LEN(p); + regval = HLL_SPARSE_VAL_VALUE(p); +- if ((runlen + idx) > HLL_REGISTERS) break; /* Overflow. */ ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + while(runlen--) { + HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval); + idx++; +@@ -625,7 +637,7 @@ int hllSparseToDense(robj *o) { + + /* If the sparse representation was valid, we expect to find idx + * set to HLL_REGISTERS. */ +- if (idx != HLL_REGISTERS) { ++ if (!valid || idx != HLL_REGISTERS) { + sdsfree(dense); + return C_ERR; + } +@@ -911,27 +923,40 @@ int hllSparseAdd(robj *o, unsigned char *ele, size_t elesize) { + void hllSparseRegHisto(uint8_t *sparse, int sparselen, int *invalid, int* reghisto) { + int idx = 0, runlen, regval; + uint8_t *end = sparse+sparselen, *p = sparse; ++ int valid = 1; + + while(p < end) { + if (HLL_SPARSE_IS_ZERO(p)) { + runlen = HLL_SPARSE_ZERO_LEN(p); ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + idx += runlen; + reghisto[0] += runlen; + p++; + } else if (HLL_SPARSE_IS_XZERO(p)) { + runlen = HLL_SPARSE_XZERO_LEN(p); ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + idx += runlen; + reghisto[0] += runlen; + p += 2; + } else { + runlen = HLL_SPARSE_VAL_LEN(p); + regval = HLL_SPARSE_VAL_VALUE(p); ++ if ((runlen + idx) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + idx += runlen; + reghisto[regval] += runlen; + p++; + } + } +- if (idx != HLL_REGISTERS && invalid) *invalid = 1; ++ if ((!valid || idx != HLL_REGISTERS) && invalid) *invalid = 1; + } + + /* ========================= HyperLogLog Count ============================== +@@ -1079,22 +1104,34 @@ int hllMerge(uint8_t *max, robj *hll) { + } else { + uint8_t *p = hll->ptr, *end = p + sdslen(hll->ptr); + long runlen, regval; ++ int valid = 1; + + p += HLL_HDR_SIZE; + i = 0; + while(p < end) { + if (HLL_SPARSE_IS_ZERO(p)) { + runlen = HLL_SPARSE_ZERO_LEN(p); ++ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + i += runlen; + p++; + } else if (HLL_SPARSE_IS_XZERO(p)) { + runlen = HLL_SPARSE_XZERO_LEN(p); ++ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + i += runlen; + p += 2; + } else { + runlen = HLL_SPARSE_VAL_LEN(p); + regval = HLL_SPARSE_VAL_VALUE(p); +- if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */ ++ if ((runlen + i) > HLL_REGISTERS) { /* Overflow. */ ++ valid = 0; ++ break; ++ } + while(runlen--) { + if (regval > max[i]) max[i] = regval; + i++; +@@ -1102,7 +1139,7 @@ int hllMerge(uint8_t *max, robj *hll) { + p++; + } + } +- if (i != HLL_REGISTERS) return C_ERR; ++ if (!valid || i != HLL_REGISTERS) return C_ERR; + } + return C_OK; + } +diff --git a/tests/unit/hyperloglog.tcl b/tests/unit/hyperloglog.tcl +index db26a2e75..a1ae52040 100644 +--- a/tests/unit/hyperloglog.tcl ++++ b/tests/unit/hyperloglog.tcl +@@ -106,6 +106,57 @@ start_server {tags {"hll"}} { + set e + } {*WRONGTYPE*} + ++ test {Corrupted sparse HyperLogLogs doesn't cause overflow and out-of-bounds with XZERO opcode} { ++ r del hll ++ ++ # Create a sparse-encoded HyperLogLog header ++ set pl [string cat "HYLL" [binary format c12 {1 0 0 0 0 0 0 0 0 0 0 0}]] ++ ++ # Create an XZERO opcode with the maximum run length of 16384(2^14) ++ set runlen [expr 16384 - 1] ++ set chunk [binary format cc [expr {0b01000000 | ($runlen >> 8)}] [expr {$runlen & 0xff}]] ++ # Fill the HLL with more than 131072(2^17) XZERO opcodes to make the total ++ # run length exceed 4GB, will cause an integer overflow. ++ set repeat [expr 131072 + 1000] ++ for {set i 0} {$i < $repeat} {incr i} { ++ append pl $chunk ++ } ++ ++ # Create a VAL opcode with a value that will cause out-of-bounds. ++ append pl [binary format c 0b11111111] ++ r set hll $pl ++ ++ # This should not overflow and out-of-bounds. ++ assert_error {*INVALIDOBJ*} {r pfcount hll hll} ++ assert_error {*INVALIDOBJ*} {r pfdebug getreg hll} ++ r ping ++ } ++ ++ test {Corrupted sparse HyperLogLogs doesn't cause overflow and out-of-bounds with ZERO opcode} { ++ r del hll ++ ++ # Create a sparse-encoded HyperLogLog header ++ set pl [string cat "HYLL" [binary format c12 {1 0 0 0 0 0 0 0 0 0 0 0}]] ++ ++ # # Create an ZERO opcode with the maximum run length of 64(2^6) ++ set chunk [binary format c [expr {0b00000000 | 0x3f}]] ++ # Fill the HLL with more than 33554432(2^17) ZERO opcodes to make the total ++ # run length exceed 4GB, will cause an integer overflow. ++ set repeat [expr 33554432 + 1000] ++ for {set i 0} {$i < $repeat} {incr i} { ++ append pl $chunk ++ } ++ ++ # Create a VAL opcode with a value that will cause out-of-bounds. ++ append pl [binary format c 0b11111111] ++ r set hll $pl ++ ++ # This should not overflow and out-of-bounds. ++ assert_error {*INVALIDOBJ*} {r pfcount hll hll} ++ assert_error {*INVALIDOBJ*} {r pfdebug getreg hll} ++ r ping ++ } ++ + test {Corrupted dense HyperLogLogs are detected: Wrong length} { + r del hll + r pfadd hll a b c +-- +2.51.0 + diff --git a/backport-CVE-2025-48367.patch b/backport-CVE-2025-48367.patch new file mode 100644 index 0000000000000000000000000000000000000000..ee4127709aa276030213f9095fd653afe37ffbac --- /dev/null +++ b/backport-CVE-2025-48367.patch @@ -0,0 +1,105 @@ +From 0fe67435935cc5724ff6eb9c4ca4120c58a15765 Mon Sep 17 00:00:00 2001 +From: Ozan Tezcan +Date: Wed, 14 May 2025 11:02:30 +0300 +Subject: [PATCH] Retry accept() even if accepted connection reports an error + (CVE-2025-48367) + +In case of accept4() returns an error, we should check errno value and +decide if we should retry accept4() without waiting next event loop iteration. +--- + src/anet.c | 24 ++++++++++++++++++++++++ + src/anet.h | 2 +- + src/cluster.c | 2 ++ + src/networking.c | 6 ++++++ + 4 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/src/anet.c b/src/anet.c +index 91f61718e..2e42fc572 100644 +--- a/src/anet.c ++++ b/src/anet.c +@@ -594,3 +594,27 @@ int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type) { + anetFdToString(fd,ip,sizeof(ip),&port,fd_to_str_type); + return anetFormatAddr(buf, buf_len, ip, port); + } ++ ++/* This function must be called after accept4() fails. It returns 1 if 'err' ++ * indicates accepted connection faced an error, and it's okay to continue ++ * accepting next connection by calling accept4() again. Other errors either ++ * indicate programming errors, e.g. calling accept() on a closed fd or indicate ++ * a resource limit has been reached, e.g. -EMFILE, open fd limit has been ++ * reached. In the latter case, caller might wait until resources are available. ++ * See accept4() documentation for details. */ ++int anetAcceptFailureNeedsRetry(int err) { ++ if (err == ECONNABORTED) ++ return 1; ++ ++#if defined(__linux__) ++ /* For details, see 'Error Handling' section on ++ * https://man7.org/linux/man-pages/man2/accept.2.html */ ++ if (err == ENETDOWN || err == EPROTO || err == ENOPROTOOPT || ++ err == EHOSTDOWN || err == ENONET || err == EHOSTUNREACH || ++ err == EOPNOTSUPP || err == ENETUNREACH) ++ { ++ return 1; ++ } ++#endif ++ return 0; ++} +diff --git a/src/anet.h b/src/anet.h +index 2a685cc01..adedaf3e1 100644 +--- a/src/anet.h ++++ b/src/anet.h +@@ -72,5 +72,5 @@ int anetFdToString(int fd, char *ip, size_t ip_len, int *port, int fd_to_str_typ + int anetKeepAlive(char *err, int fd, int interval); + int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port); + int anetFormatFdAddr(int fd, char *buf, size_t buf_len, int fd_to_str_type); +- ++int anetAcceptFailureNeedsRetry(int err); + #endif +diff --git a/src/cluster.c b/src/cluster.c +index 8807fe2c8..030897c6a 100644 +--- a/src/cluster.c ++++ b/src/cluster.c +@@ -691,6 +691,8 @@ void clusterAcceptHandler(aeEventLoop *el, int fd, void *privdata, int mask) { + while(max--) { + cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); + if (cfd == ANET_ERR) { ++ if (anetAcceptFailureNeedsRetry(errno)) ++ continue; + if (errno != EWOULDBLOCK) + serverLog(LL_VERBOSE, + "Error accepting cluster node: %s", server.neterr); +diff --git a/src/networking.c b/src/networking.c +index 11891d3e9..2598a58ba 100644 +--- a/src/networking.c ++++ b/src/networking.c +@@ -1190,6 +1190,8 @@ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) { + while(max--) { + cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); + if (cfd == ANET_ERR) { ++ if (anetAcceptFailureNeedsRetry(errno)) ++ continue; + if (errno != EWOULDBLOCK) + serverLog(LL_WARNING, + "Accepting client connection: %s", server.neterr); +@@ -1211,6 +1213,8 @@ void acceptTLSHandler(aeEventLoop *el, int fd, void *privdata, int mask) { + while(max--) { + cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport); + if (cfd == ANET_ERR) { ++ if (anetAcceptFailureNeedsRetry(errno)) ++ continue; + if (errno != EWOULDBLOCK) + serverLog(LL_WARNING, + "Accepting client connection: %s", server.neterr); +@@ -1231,6 +1235,8 @@ void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) { + while(max--) { + cfd = anetUnixAccept(server.neterr, fd); + if (cfd == ANET_ERR) { ++ if (anetAcceptFailureNeedsRetry(errno)) ++ continue; + if (errno != EWOULDBLOCK) + serverLog(LL_WARNING, + "Accepting client connection: %s", server.neterr); +-- +2.51.0 + diff --git a/CVE-2025-49844.patch b/backport-CVE-2025-49844.patch similarity index 89% rename from CVE-2025-49844.patch rename to backport-CVE-2025-49844.patch index af548af38b9f8febb5359d1bd9e85e788ad0dc93..bec45853652f076804d8cb2a6b86add701487ce8 100644 --- a/CVE-2025-49844.patch +++ b/backport-CVE-2025-49844.patch @@ -1,32 +1,35 @@ -From d5728cb5795c966c5b5b1e0f0ac576a7e69af539 Mon Sep 17 00:00:00 2001 -From: Mincho Paskalev -Date: Mon, 23 Jun 2025 11:41:37 +0300 -Subject: [PATCH] Lua script may lead to remote code execution (CVE-2025-49844) - ---- - deps/lua/src/lparser.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/deps/lua/src/lparser.c b/deps/lua/src/lparser.c -index dda7488dcad..ee7d90c90d7 100644 ---- a/deps/lua/src/lparser.c -+++ b/deps/lua/src/lparser.c -@@ -384,13 +384,17 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { - struct LexState lexstate; - struct FuncState funcstate; - lexstate.buff = buff; -- luaX_setinput(L, &lexstate, z, luaS_new(L, name)); -+ TString *tname = luaS_new(L, name); -+ setsvalue2s(L, L->top, tname); -+ incr_top(L); -+ luaX_setinput(L, &lexstate, z, tname); - open_func(&lexstate, &funcstate); - funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ - luaX_next(&lexstate); /* read first token */ - chunk(&lexstate); - check(&lexstate, TK_EOS); - close_func(&lexstate); -+ --L->top; - lua_assert(funcstate.prev == NULL); - lua_assert(funcstate.f->nups == 0); - lua_assert(lexstate.fs == NULL); +From 5785f3e6e5aa13a9f0e5e1576b398eb4f7d3bb13 Mon Sep 17 00:00:00 2001 +From: Mincho Paskalev +Date: Mon, 23 Jun 2025 11:41:37 +0300 +Subject: [PATCH] Lua script may lead to remote code execution (CVE-2025-49844) + +--- + deps/lua/src/lparser.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/deps/lua/src/lparser.c b/deps/lua/src/lparser.c +index dda7488dc..ee7d90c90 100644 +--- a/deps/lua/src/lparser.c ++++ b/deps/lua/src/lparser.c +@@ -384,13 +384,17 @@ Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + struct LexState lexstate; + struct FuncState funcstate; + lexstate.buff = buff; +- luaX_setinput(L, &lexstate, z, luaS_new(L, name)); ++ TString *tname = luaS_new(L, name); ++ setsvalue2s(L, L->top, tname); ++ incr_top(L); ++ luaX_setinput(L, &lexstate, z, tname); + open_func(&lexstate, &funcstate); + funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + check(&lexstate, TK_EOS); + close_func(&lexstate); ++ --L->top; + lua_assert(funcstate.prev == NULL); + lua_assert(funcstate.f->nups == 0); + lua_assert(lexstate.fs == NULL); +-- +2.51.0 + diff --git a/redis6.spec b/redis6.spec index 49b6bd22f5f7c6d72eb9af8bad31a6bea310c243..9d396b2b28c1946a310a4e6af47c09e574f8d195 100644 --- a/redis6.spec +++ b/redis6.spec @@ -6,7 +6,7 @@ %global Pname redis Name: redis6 Version: 6.2.7 -Release: 4 +Release: 5 Summary: A persistent key-value database License: BSD and MIT URL: https://redis.io @@ -22,7 +22,12 @@ Source10: https://github.com/%{Pname}/%{Pname}-doc/archive/%{doc_comm Patch0001: Modify-aarch64-architecture-jemalloc-page-size-from-from-4k-to-64k.patch Patch0003: Add-loongarch64-support.patch Patch0004: Update-config.guess-and-config.sub.patch -Patch0005: CVE-2025-49844.patch + +# backport upstream +Patch3000: backport-CVE-2025-21605.patch +Patch3001: backport-CVE-2025-32023.patch +Patch3002: backport-CVE-2025-48367.patch +Patch3003: backport-CVE-2025-49844.patch BuildRequires: make gcc %if %{with tests} @@ -89,7 +94,12 @@ tar -xvf %{SOURCE10} %patch0003 -p1 %patch0004 -p1 %endif -%patch0005 -p1 +# backport upstream +%patch3000 -p1 +%patch3001 -p1 +%patch3002 -p1 +%patch3003 -p1 + mv ../%{Pname}-doc-%{doc_commit} doc mv deps/lua/COPYRIGHT COPYRIGHT-lua mv deps/jemalloc/COPYING COPYING-jemalloc @@ -218,6 +228,9 @@ fi %{_docdir}/%{Pname} %changelog +* Thu Dec 04 2025 jiangxinyu - 6.2.7-5 +- Fix CVE-2025-21605 CVE-2025-32023 CVE-2025-48367 + * Thu Oct 09 2025 zhangliangpengkun - 6.2.7-4 - fix CVE-2025-49844