redis: fix CVE-2026-23631

This patch applies the upstream fix as referenced in [2],
using the commit shown in [1].

[1] https://github.com/redis/redis/commit/0cca172a174642bdae03b871615227896274d9bb
[2] https://nvd.nist.gov/vuln/detail/CVE-2026-23631

Signed-off-by: Yogita Urade <yurade@cisco.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Yogita Urade authored and Anuj Mittal committed 2026-10-28 12:31:46 +05:30
1 parent 0d975df7da
commit 77ca1bfa10
2 files changed
+120

No files matched your search

@@ -0,0 +1,119 @@
From 0cca172a174642bdae03b871615227896274d9bb Mon Sep 17 00:00:00 2001
From: Ozan Tezcan <ozantezcan@gmail.com>
Date: Tue, 14 Apr 2026 08:47:04 +0300
Subject: [PATCH] Fix use-after-free when fullsync happens while replica is
running a timed out script (CVE-2026-23631)
Fullsync triggers emptyData and scriptingReset which free the scripting/function engine. If a timed out script is still running on the replica, this causes a use-after-free. Delay fullsync processing in readSyncBulkPayload until the script finishes.
CVE: CVE-2026-23631
Upstream-Status: Backport [https://github.com/redis/redis/commit/0cca172a174642bdae03b871615227896274d9bb]
(cherry picked from commit 0cca172a174642bdae03b871615227896274d9bb)
Signed-off-by: Yogita Urade <yurade@cisco.com>
---
src/replication.c | 5 ++
tests/integration/replication.tcl | 77 +++++++++++++++++++++++++++++++
2 files changed, 82 insertions(+)
diff --git a/src/replication.c b/src/replication.c
index 5fab23b1c..489032aed 100644
--- a/src/replication.c
+++ b/src/replication.c
@@ -2022,6 +2022,11 @@ void replicationAttachToNewMaster(void) {
/* Asynchronously read the SYNC payload we receive from a master */
#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */
void readSyncBulkPayload(connection *conn) {
+ /* During full sync, the functions engine is freed right before loading
+ * the RDB. To avoid this happening while a function is still running,
+ * delay full sync processing until it finishes. */
+ if (isInsideYieldingLongCommand()) return;
+
char buf[PROTO_IOBUF_LEN];
ssize_t nread, readlen, nwritten;
int use_diskless_load = useDisklessLoad();
diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl
index 65494f427..412a00740 100644
--- a/tests/integration/replication.tcl
+++ b/tests/integration/replication.tcl
@@ -1661,3 +1661,80 @@ start_server {tags {"repl external:skip"}} {
}
}
}
+
+# Fullsync should not free the functions lib ctx while the replica has
+# a timed out function that is still running.
+foreach type {script function} {
+ start_server {tags {"repl external:skip"}} {
+ start_server {} {
+ set master [srv -1 client]
+ set master_host [srv -1 host]
+ set master_port [srv -1 port]
+ set replica [srv 0 client]
+
+ test "Fullsync should not free scripting engine on a replica while a $type is running" {
+ $master config set repl-diskless-sync yes
+ $master config set repl-diskless-sync-delay 0
+ # Set small client output buffer limit to trigger fullsync quickly
+ $master config set client-output-buffer-limit "replica 1k 1k 0"
+ $replica config set repl-diskless-load yes
+ $replica config set busy-reply-threshold 1 ;# script timeout in 1 ms
+
+ # Load function
+ if {$type eq "function"} {
+ $master function load replace {#!lua name=blocklib
+ redis.register_function{
+ function_name='blockfunc',
+ callback=function() while true do end end,
+ flags={'no-writes'}
+ }
+ }
+ }
+
+ # Start replication
+ $replica replicaof $master_host $master_port
+ wait_for_sync $replica
+
+ # Run the blocking script on replica
+ set rd [redis_deferring_client]
+ if {$type eq "script"} {
+ $rd eval {while true do end} 0
+ } else {
+ $rd fcall_ro blockfunc 0
+ }
+
+ # Verify replica replies with BUSY
+ wait_for_condition 50 100 {
+ [catch {$replica ping} e] == 1 && [string match {*BUSY*} $e]
+ } else {
+ fail "$type didn't become busy"
+ }
+
+ # Fills client output buffer and triggers fullsync
+ populate 5 bigkey 1000000 -1
+ wait_for_condition 50 100 {
+ [s -1 sync_full] >= 2
+ } else {
+ fail "Fullsync was not triggered"
+ }
+
+ # Verify replica is still running the function
+ after 1000
+ catch {$replica ping} e
+ assert_match {*BUSY*} $e "replica should still reply with BUSY"
+
+ if {$type eq "script"} {
+ $replica script kill
+ } else {
+ $replica function kill
+ }
+
+ # Verify replica is responsive again
+ catch {$rd read} result
+ $rd close
+ wait_for_sync $replica
+ assert_equal [$replica ping] "PONG"
+ }
+ }
+ }
+}
@@ -17,6 +17,7 @@ SRC_URI = "http://download.redis.io/releases/${BP}.tar.gz \
file://0005-Define-_GNU_SOURCE-to-get-PTHREAD_MUTEX_INITIALIZER.patch \
file://CVE-2026-81934.patch \
file://CVE-2026-23479.patch \
file://CVE-2026-23631.patch \
"
SRC_URI[sha256sum] = "6d0a9913887a4972536f9da226f1575859c34d86354129163260a5f9c6bd4229"