Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package valkey for openSUSE:Factory checked in at 2026-10-01 16:59:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/valkey (Old) and /work/SRC/openSUSE:Factory/.valkey.new.1253 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "valkey" Thu Oct 1 16:59:44 2026 rev:25 rq:1381537 version:9.1.2 Changes: -------- --- /work/SRC/openSUSE:Factory/valkey/valkey.changes 2026-09-17 15:23:29.146210616 +0200 +++ /work/SRC/openSUSE:Factory/.valkey.new.1253/valkey.changes 2026-10-01 16:59:46.214993435 +0200 @@ -1,0 +2,8 @@ +Tue Sep 29 18:52:37 UTC 2026 - Antonio Teixeira <[email protected]> + +- Fix CVE-2026-92925, failure to properly validate string-carrying extensions + for null-termination in the cluster bus packet parser can lead to an + out-of-bounds read (bsc#1281417) + * CVE-2026-92925.patch + +------------------------------------------------------------------- New: ---- CVE-2026-92925.patch ----------(New B)---------- New: out-of-bounds read (bsc#1281417) * CVE-2026-92925.patch ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ valkey.spec ++++++ --- /var/tmp/diff_new_pack.5xtvOk/_old 2026-10-01 16:59:47.049028372 +0200 +++ /var/tmp/diff_new_pack.5xtvOk/_new 2026-10-01 16:59:47.050028413 +0200 @@ -43,6 +43,12 @@ Source9: %{name}-user.conf Source10: macros.%{name} Source11: migrate_redis_to_valkey.bash +# PATCH-FIX-OPENSUSE bsc#1281417 CVE-2026-92925 +# failure to properly validate string-carrying extensions for null-termination in the cluster bus packet parser can lead to an out-of-bounds read +# Based on upstream PRs still under review: +# https://github.com/valkey-io/valkey/pull/4661 +# https://github.com/valkey-io/valkey/pull/4662 +Patch0: CVE-2026-92925.patch # PATCH-FIX-OPENSUSE -- Adjust configs for openSUSE Patch1001: %{name}-conf.patch BuildRequires: jemalloc-devel ++++++ CVE-2026-92925.patch ++++++ diff --git a/src/cluster_legacy.c b/src/cluster_legacy.c index 3bdc40e9d..83a807b8e 100644 --- a/src/cluster_legacy.c +++ b/src/cluster_legacy.c @@ -3369,6 +3369,24 @@ static uint32_t getForgottenNodeExtSize(void) { return getAlignedPingExtSize(sizeof(clusterMsgPingExtForgottenNode)); } +/* Return the minimum encoded size for an extension type. Unknown extensions + * only require the common header to preserve forward compatibility. */ +static uint32_t getMinimumPingExtSize(uint16_t type) { + switch (type) { + case CLUSTERMSG_EXT_TYPE_HOSTNAME: return getAlignedPingExtSize(sizeof(clusterMsgPingExtHostname)); + case CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME: return getAlignedPingExtSize(sizeof(clusterMsgPingExtHumanNodename)); + case CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE: return getForgottenNodeExtSize(); + case CLUSTERMSG_EXT_TYPE_SHARDID: return getShardIdPingExtSize(); + case CLUSTERMSG_EXT_TYPE_CLIENT_IPV4: return getAlignedPingExtSize(sizeof(clusterMsgPingExtClientIpV4)); + case CLUSTERMSG_EXT_TYPE_CLIENT_IPV6: return getAlignedPingExtSize(sizeof(clusterMsgPingExtClientIpV6)); + case CLUSTERMSG_EXT_TYPE_CLIENT_PORT: return getAlignedPingExtSize(sizeof(clusterMsgPingExtClientPort)); + case CLUSTERMSG_EXT_TYPE_CLIENT_TLS_PORT: return getAlignedPingExtSize(sizeof(clusterMsgPingExtClientTlsPort)); + case CLUSTERMSG_EXT_TYPE_AVAILABILITY_ZONE: + return getAlignedPingExtSize(sizeof(clusterMsgPingExtAvailabilityZone)); + default: return sizeof(clusterMsgPingExt); + } +} + static void *preparePingExt(clusterMsgPingExt *ext, uint16_t type, uint32_t length) { ext->type = htons(type); ext->length = htonl(length); @@ -3720,6 +3738,13 @@ int clusterIsValidPacket(clusterLink *link) { return 0; } + if (!(msg->mflags[0] & CLUSTERMSG_FLAG0_EXT_DATA) && extensions != 0) { + serverLog(LL_WARNING, + "Received invalid %s packet with %d extensions but no extension data flag", + clusterGetMessageTypeString(type), extensions); + return 0; + } + /* If there is extension data, which doesn't have a fixed length, * loop through them and validate the length of it now. */ if (msg->mflags[0] & CLUSTERMSG_FLAG0_EXT_DATA) { @@ -3740,6 +3765,14 @@ int clusterIsValidPacket(clusterLink *link) { clusterGetMessageTypeString(type), (int)extlen); return 0; } + uint16_t ext_type = ntohs(ext->type); + uint32_t min_extlen = getMinimumPingExtSize(ext_type); + if (extlen < min_extlen) { + serverLog(LL_WARNING, + "Received invalid %s packet with extension type %d that is %d bytes but requires at least %d", + clusterGetMessageTypeString(type), (int)ext_type, (int)extlen, (int)min_extlen); + return 0; + } /* Similar check to earlier, but we want to make sure the extension length is valid * this time. */ if ((totlen - explen) < extlen) { @@ -3749,6 +3782,20 @@ int clusterIsValidPacket(clusterLink *link) { clusterGetMessageTypeString(type), (unsigned long long)totlen); return 0; } + bool is_string_ext = ext_type == CLUSTERMSG_EXT_TYPE_HOSTNAME || + ext_type == CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME || + ext_type == CLUSTERMSG_EXT_TYPE_CLIENT_IPV4 || + ext_type == CLUSTERMSG_EXT_TYPE_CLIENT_IPV6 || + ext_type == CLUSTERMSG_EXT_TYPE_AVAILABILITY_ZONE; + /* String extensions are consumed as C strings. Make sure those reads + * cannot continue past the declared extension. */ + if (is_string_ext && + (extlen <= sizeof(clusterMsgPingExt) || + memchr((char *)ext + sizeof(clusterMsgPingExt), '\0', extlen - sizeof(clusterMsgPingExt)) == NULL)) { + serverLog(LL_WARNING, "Received invalid %s packet with unterminated string extension type %d", + clusterGetMessageTypeString(type), (int)ext_type); + return 0; + } explen += extlen; ext = getNextPingExt(ext); } diff --git a/tests/unit/cluster/packet.tcl b/tests/unit/cluster/packet.tcl index ab3313eb5..4618bd30a 100644 --- a/tests/unit/cluster/packet.tcl +++ b/tests/unit/cluster/packet.tcl @@ -213,6 +213,113 @@ start_cluster 1 0 {tags {external:skip cluster tls:skip}} { } } +start_cluster 1 0 {tags {external:skip cluster tls:skip}} { + test "Reject extension count without extension data flag" { + set base_port [srv 0 port] + set cluster_port [expr {$base_port + 10000}] + set sender_node_id [R 0 cluster myid] + + set packet [create_cluster_meet_packet \ + $sender_node_id $base_port $cluster_port 0 1 0] + + set loglines [count_log_lines 0] + set sock [socket 127.0.0.1 $cluster_port] + fconfigure $sock -translation binary -buffering none -blocking 1 + puts -nonewline $sock $packet + flush $sock + close $sock + + wait_for_log_messages 0 \ + [list "*Received invalid meet packet with 1 extensions but no extension data flag*"] \ + $loglines 1000 10 + assert_equal "PONG" [R 0 ping] + } + + test "Reject cluster bus extensions shorter than their type requires" { + set base_port [srv 0 port] + set cluster_port [expr {$base_port + 10000}] + set sender_node_id [R 0 cluster myid] + + foreach extension_type {0 1 2 3 4 5 6 7 8} { + set packet [create_cluster_meet_packet \ + $sender_node_id $base_port $cluster_port 0 1 4] + + # Append only the common eight-byte extension header. Every known + # extension type requires an additional data payload. + append packet [binary format I 8] + append packet [binary format S $extension_type] + append packet [binary format S 0] + set packet [string replace $packet 4 7 [binary format I [string length $packet]]] + + set loglines [count_log_lines 0] + set sock [socket 127.0.0.1 $cluster_port] + fconfigure $sock -translation binary -buffering none -blocking 1 + puts -nonewline $sock $packet + flush $sock + close $sock + + wait_for_log_messages 0 \ + [list "*Received invalid meet packet with extension type $extension_type that is 8 bytes*"] \ + $loglines 1000 10 + assert_equal "PONG" [R 0 ping] + } + } + + test "Accept header-only unknown cluster bus extensions" { + set base_port [srv 0 port] + set cluster_port [expr {$base_port + 10000}] + set sender_node_id [R 0 cluster myid] + + set packet [create_cluster_meet_packet \ + $sender_node_id $base_port $cluster_port 0 1 4] + append packet [binary format I 8] + append packet [binary format S 10] + append packet [binary format S 0] + set packet [string replace $packet 4 7 [binary format I [string length $packet]]] + + set loglines [count_log_lines 0] + set sock [socket 127.0.0.1 $cluster_port] + fconfigure $sock -translation binary -buffering none -blocking 1 + puts -nonewline $sock $packet + flush $sock + close $sock + + wait_for_log_messages 0 [list "*Received unknown extension type 10*"] $loglines 1000 10 + assert_equal "PONG" [R 0 ping] + } + + test "Reject unterminated string extensions in cluster bus packets" { + set base_port [srv 0 port] + set cluster_port [expr {$base_port + 10000}] + set sender_node_id [R 0 cluster myid] + + foreach extension_type {0 1 4 5 8} { + set packet [create_cluster_meet_packet \ + $sender_node_id $base_port $cluster_port 0 1 4] + + # Append an aligned 16-byte string extension whose eight data bytes + # contain no NUL terminator. + append packet [binary format I 16] + append packet [binary format S $extension_type] + append packet [binary format S 0] + append packet "abcdefgh" + set packet [string replace $packet 4 7 [binary format I [string length $packet]]] + + set loglines [count_log_lines 0] + set sock [socket 127.0.0.1 $cluster_port] + fconfigure $sock -translation binary -buffering none -blocking 1 + puts -nonewline $sock $packet + flush $sock + close $sock + + wait_for_log_messages 0 \ + [list "*Received invalid meet packet with unterminated string extension type $extension_type*"] \ + $loglines 1000 10 + assert_equal "PONG" [R 0 ping] + } + } +} + start_cluster 1 0 {tags {external:skip cluster tls:skip}} { test "Packet with missing gossip messages don't cause invalid read" { set base_port [srv 0 port]
