From e92f32e6b9b7d25fefada8afdf542f42a64e192d Mon Sep 17 00:00:00 2001 From: Kartik Kenchi Date: Wed, 1 Jul 2026 15:27:34 +0530 Subject: [PATCH 1/2] fix redis: guard empty array reply in OnPsubscribeReply --- redis/src/storages/redis/impl/sentinel.cpp | 2 +- .../src/storages/redis/impl/sentinel_test.cpp | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/redis/src/storages/redis/impl/sentinel.cpp b/redis/src/storages/redis/impl/sentinel.cpp index 263e0981c5de..8474f29b4c61 100644 --- a/redis/src/storages/redis/impl/sentinel.cpp +++ b/redis/src/storages/redis/impl/sentinel.cpp @@ -355,7 +355,7 @@ void Sentinel::OnPsubscribeReply( return; } const auto& reply_array = reply->data.GetArray(); - if (!reply_array[0].IsString()) { + if (reply_array.empty() || !reply_array[0].IsString()) { return; } if (!strcasecmp(reply_array[0].GetString().c_str(), "PSUBSCRIBE")) { diff --git a/redis/src/storages/redis/impl/sentinel_test.cpp b/redis/src/storages/redis/impl/sentinel_test.cpp index c3d4efaa31a1..6373c5d04bdb 100644 --- a/redis/src/storages/redis/impl/sentinel_test.cpp +++ b/redis/src/storages/redis/impl/sentinel_test.cpp @@ -1,8 +1,12 @@ #include #include +#include + #include +#include + USERVER_NAMESPACE_BEGIN TEST(Sentinel, CreateTmpKey) { @@ -14,4 +18,25 @@ TEST(Sentinel, CreateTmpKey) { } } +TEST(Sentinel, OnPsubscribeReplyEmptyArray) { + using storages::redis::Reply; + using storages::redis::ReplyData; + using storages::redis::impl::Sentinel; + + auto reply = std::make_shared("PSUBSCRIBE", ReplyData{ReplyData::Array{}}); + ASSERT_TRUE(reply->data.IsArray()); + ASSERT_TRUE(reply->data.GetArray().empty()); + + const auto fail_pmessage = [](storages::redis::ServerId, const std::string&, const std::string&, + const std::string&) { FAIL() << "pmessage callback must not fire on empty array"; }; + const auto fail_subscribe = [](storages::redis::ServerId, const std::string&, size_t) { + FAIL() << "subscribe callback must not fire on empty array"; + }; + const auto fail_unsubscribe = [](storages::redis::ServerId, const std::string&, size_t) { + FAIL() << "unsubscribe callback must not fire on empty array"; + }; + + Sentinel::OnPsubscribeReply(fail_pmessage, fail_subscribe, fail_unsubscribe, reply); +} + USERVER_NAMESPACE_END From 12653a4b837dfdcd36717671241570e48e90d9db Mon Sep 17 00:00:00 2001 From: Kartik Kenchi Date: Thu, 2 Jul 2026 14:13:37 +0530 Subject: [PATCH 2/2] test redis: cover too-short pub/sub arrays in OnPsubscribeReply --- .../src/storages/redis/impl/sentinel_test.cpp | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/redis/src/storages/redis/impl/sentinel_test.cpp b/redis/src/storages/redis/impl/sentinel_test.cpp index 6373c5d04bdb..de9d1e0bda58 100644 --- a/redis/src/storages/redis/impl/sentinel_test.cpp +++ b/redis/src/storages/redis/impl/sentinel_test.cpp @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include @@ -39,4 +42,40 @@ TEST(Sentinel, OnPsubscribeReplyEmptyArray) { Sentinel::OnPsubscribeReply(fail_pmessage, fail_subscribe, fail_unsubscribe, reply); } +TEST(Sentinel, OnPsubscribeReplyTooShortArray) { + using storages::redis::Reply; + using storages::redis::ReplyData; + using storages::redis::impl::Sentinel; + + const auto fail_pmessage = [](storages::redis::ServerId, const std::string&, const std::string&, + const std::string&) { FAIL() << "pmessage callback must not fire on malformed array"; }; + const auto fail_subscribe = [](storages::redis::ServerId, const std::string&, size_t) { + FAIL() << "subscribe callback must not fire on malformed array"; + }; + const auto fail_unsubscribe = [](storages::redis::ServerId, const std::string&, size_t) { + FAIL() << "unsubscribe callback must not fire on malformed array"; + }; + + // A server/proxy may answer with a well-formed array that is missing the + // channel/count fields. Each of these must be ignored, not indexed OOB. + const auto make_array = [](std::vector parts) { + ReplyData::Array array; + for (auto& part : parts) array.emplace_back(std::move(part)); + return array; + }; + + for (auto& parts : std::vector>{ + {"PSUBSCRIBE"}, // command only, no channel/count + {"PSUBSCRIBE", "news.*"}, // missing count + {"PUNSUBSCRIBE"}, // command only + {"PUNSUBSCRIBE", "news.*"}, // missing count + {"PMESSAGE"}, // command only + {"PMESSAGE", "news.*"}, // missing channel/payload + {"PMESSAGE", "news.*", "news.tech"}, // missing payload + }) { + auto reply = std::make_shared("PSUBSCRIBE", ReplyData{make_array(std::move(parts))}); + Sentinel::OnPsubscribeReply(fail_pmessage, fail_subscribe, fail_unsubscribe, reply); + } +} + USERVER_NAMESPACE_END