From 89444f7149cb93354a98543c3d763b173a15e5c5 Mon Sep 17 00:00:00 2001 From: u6th9d Date: Fri, 10 Nov 2023 10:33:10 +0800 Subject: [PATCH] bugfix: set/zset/hash member may overflow (#2106) * Update base_meta_value_format.h * Update redis_hashes.cc * Update redis_sets.cc * Update redis_zsets.cc --- src/storage/src/base_meta_value_format.h | 16 +++++++++++++ src/storage/src/redis_hashes.cc | 21 +++++++++++++++++ src/storage/src/redis_sets.cc | 27 +++++++++++++++++++++ src/storage/src/redis_zsets.cc | 30 ++++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/src/storage/src/base_meta_value_format.h b/src/storage/src/base_meta_value_format.h index e940456242..10c200f8d1 100644 --- a/src/storage/src/base_meta_value_format.h +++ b/src/storage/src/base_meta_value_format.h @@ -89,6 +89,13 @@ class ParsedBaseMetaValue : public ParsedInternalValue { int32_t count() { return count_; } + bool check_set_count(size_t count) { + if (count > INT32_MAX) { + return false; + } + return true; + } + void set_count(int32_t count) { count_ = count; if (value_) { @@ -97,6 +104,15 @@ class ParsedBaseMetaValue : public ParsedInternalValue { } } + bool CheckModifyCount(int32_t delta) { + int64_t count = count_; + count += delta; + if (count < 0 || count > INT32_MAX) { + return false; + } + return true; + } + void ModifyCount(int32_t delta) { count_ += delta; if (value_) { diff --git a/src/storage/src/redis_hashes.cc b/src/storage/src/redis_hashes.cc index b73b3cf912..5499708986 100644 --- a/src/storage/src/redis_hashes.cc +++ b/src/storage/src/redis_hashes.cc @@ -234,6 +234,9 @@ Status RedisHashes::HDel(const Slice& key, const std::vector& field } } *ret = del_cnt; + if (!parsed_hashes_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); } @@ -348,6 +351,9 @@ Status RedisHashes::HIncrby(const Slice& key, const Slice& field, int64_t value, statistic++; } else if (s.IsNotFound()) { Int64ToStr(value_buf, 32, value); + if (!parsed_hashes_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(1); batch.Put(handles_[0], key, meta_value); batch.Put(handles_[1], hashes_data_key.Encode(), value_buf); @@ -421,6 +427,9 @@ Status RedisHashes::HIncrbyfloat(const Slice& key, const Slice& field, const Sli statistic++; } else if (s.IsNotFound()) { LongDoubleToStr(long_double_by, new_value); + if (!parsed_hashes_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(1); batch.Put(handles_[0], key, meta_value); batch.Put(handles_[1], hashes_data_key.Encode(), *new_value); @@ -561,6 +570,9 @@ Status RedisHashes::HMSet(const Slice& key, const std::vector& fvs) ParsedHashesMetaValue parsed_hashes_meta_value(&meta_value); if (parsed_hashes_meta_value.IsStale() || parsed_hashes_meta_value.count() == 0) { version = parsed_hashes_meta_value.InitialMetaValue(); + if (!parsed_hashes_meta_value.check_set_count(static_cast(filtered_fvs.size()))) { + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.set_count(static_cast(filtered_fvs.size())); batch.Put(handles_[0], key, meta_value); for (const auto& fv : filtered_fvs) { @@ -584,6 +596,9 @@ Status RedisHashes::HMSet(const Slice& key, const std::vector& fvs) return s; } } + if (!parsed_hashes_meta_value.CheckModifyCount(count)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(count); batch.Put(handles_[0], key, meta_value); } @@ -634,6 +649,9 @@ Status RedisHashes::HSet(const Slice& key, const Slice& field, const Slice& valu statistic++; } } else if (s.IsNotFound()) { + if (!parsed_hashes_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(1); batch.Put(handles_[0], key, meta_value); batch.Put(handles_[1], hashes_data_key.Encode(), value); @@ -683,6 +701,9 @@ Status RedisHashes::HSetnx(const Slice& key, const Slice& field, const Slice& va if (s.ok()) { *ret = 0; } else if (s.IsNotFound()) { + if (!parsed_hashes_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("hash size overflow"); + } parsed_hashes_meta_value.ModifyCount(1); batch.Put(handles_[0], key, meta_value); batch.Put(handles_[1], hashes_data_key.Encode(), value); diff --git a/src/storage/src/redis_sets.cc b/src/storage/src/redis_sets.cc index afd2758a15..308e6278f3 100644 --- a/src/storage/src/redis_sets.cc +++ b/src/storage/src/redis_sets.cc @@ -217,6 +217,9 @@ rocksdb::Status RedisSets::SAdd(const Slice& key, const std::vector ParsedSetsMetaValue parsed_sets_meta_value(&meta_value); if (parsed_sets_meta_value.IsStale() || parsed_sets_meta_value.count() == 0) { version = parsed_sets_meta_value.InitialMetaValue(); + if (!parsed_sets_meta_value.check_set_count(static_cast(filtered_members.size()))) { + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.set_count(static_cast(filtered_members.size())); batch.Put(handles_[0], key, meta_value); for (const auto& member : filtered_members) { @@ -243,6 +246,9 @@ rocksdb::Status RedisSets::SAdd(const Slice& key, const std::vector if (cnt == 0) { return rocksdb::Status::OK(); } else { + if (!parsed_sets_meta_value.CheckModifyCount(cnt)){ + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.ModifyCount(cnt); batch.Put(handles_[0], key, meta_value); } @@ -420,6 +426,9 @@ rocksdb::Status RedisSets::SDiffstore(const Slice& destination, const std::vecto ParsedSetsMetaValue parsed_sets_meta_value(&meta_value); statistic = parsed_sets_meta_value.count(); version = parsed_sets_meta_value.InitialMetaValue(); + if (!parsed_sets_meta_value.check_set_count(static_cast(members.size()))) { + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.set_count(static_cast(members.size())); batch.Put(handles_[0], destination, meta_value); } else if (s.IsNotFound()) { @@ -603,6 +612,9 @@ rocksdb::Status RedisSets::SInterstore(const Slice& destination, const std::vect ParsedSetsMetaValue parsed_sets_meta_value(&meta_value); statistic = parsed_sets_meta_value.count(); version = parsed_sets_meta_value.InitialMetaValue(); + if (!parsed_sets_meta_value.check_set_count(static_cast(members.size()))) { + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.set_count(static_cast(members.size())); batch.Put(handles_[0], destination, meta_value); } else if (s.IsNotFound()) { @@ -714,6 +726,9 @@ rocksdb::Status RedisSets::SMove(const Slice& source, const Slice& destination, s = db_->Get(default_read_options_, handles_[1], sets_member_key.Encode(), &member_value); if (s.ok()) { *ret = 1; + if (!parsed_sets_meta_value.CheckModifyCount(-1)){ + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.ModifyCount(-1); batch.Put(handles_[0], source, meta_value); batch.Delete(handles_[1], sets_member_key.Encode()); @@ -747,6 +762,9 @@ rocksdb::Status RedisSets::SMove(const Slice& source, const Slice& destination, SetsMemberKey sets_member_key(destination, version, member); s = db_->Get(default_read_options_, handles_[1], sets_member_key.Encode(), &member_value); if (s.IsNotFound()) { + if (!parsed_sets_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.ModifyCount(1); batch.Put(handles_[0], destination, meta_value); batch.Put(handles_[1], sets_member_key.Encode(), Slice()); @@ -843,6 +861,9 @@ rocksdb::Status RedisSets::SPop(const Slice& key, std::vector* memb } } + if (!parsed_sets_meta_value.CheckModifyCount(static_cast(-cnt))){ + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.ModifyCount(static_cast(-cnt)); batch.Put(handles_[0], key, meta_value); delete iter; @@ -974,6 +995,9 @@ rocksdb::Status RedisSets::SRem(const Slice& key, const std::vector } } *ret = cnt; + if (!parsed_sets_meta_value.CheckModifyCount(-cnt)){ + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.ModifyCount(-cnt); batch.Put(handles_[0], key, meta_value); } @@ -1086,6 +1110,9 @@ rocksdb::Status RedisSets::SUnionstore(const Slice& destination, const std::vect ParsedSetsMetaValue parsed_sets_meta_value(&meta_value); statistic = parsed_sets_meta_value.count(); version = parsed_sets_meta_value.InitialMetaValue(); + if (!parsed_sets_meta_value.check_set_count(static_cast(members.size()))) { + return Status::InvalidArgument("set size overflow"); + } parsed_sets_meta_value.set_count(static_cast(members.size())); batch.Put(handles_[0], destination, meta_value); } else if (s.IsNotFound()) { diff --git a/src/storage/src/redis_zsets.cc b/src/storage/src/redis_zsets.cc index e057d4ebad..67d38ebd2d 100644 --- a/src/storage/src/redis_zsets.cc +++ b/src/storage/src/redis_zsets.cc @@ -242,6 +242,9 @@ Status RedisZSets::ZPopMax(const Slice& key, const int64_t count, std::vectorkey()); } delete iter; + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); s = db_->Write(default_write_options_, &batch); @@ -284,6 +287,9 @@ Status RedisZSets::ZPopMin(const Slice& key, const int64_t count, std::vectorkey()); } delete iter; + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); s = db_->Write(default_write_options_, &batch); @@ -360,6 +366,9 @@ Status RedisZSets::ZAdd(const Slice& key, const std::vector& score_ cnt++; } } + if (!parsed_zsets_meta_value.CheckModifyCount(cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(cnt); batch.Put(handles_[0], key, meta_value); *ret = cnt; @@ -494,6 +503,9 @@ Status RedisZSets::ZIncrby(const Slice& key, const Slice& member, double increme statistic++; } else if (s.IsNotFound()) { score = increment; + if (!parsed_zsets_meta_value.CheckModifyCount(1)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(1); batch.Put(handles_[0], key, meta_value); } else { @@ -717,6 +729,9 @@ Status RedisZSets::ZRem(const Slice& key, const std::vector& member } } *ret = del_cnt; + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); } @@ -768,6 +783,9 @@ Status RedisZSets::ZRemrangebyrank(const Slice& key, int32_t start, int32_t stop } delete iter; *ret = del_cnt; + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); } @@ -832,6 +850,9 @@ Status RedisZSets::ZRemrangebyscore(const Slice& key, double min, double max, bo } delete iter; *ret = del_cnt; + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); } @@ -1093,6 +1114,9 @@ Status RedisZSets::ZUnionstore(const Slice& destination, const std::vector(member_score_map.size()))) { + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.set_count(static_cast(member_score_map.size())); batch.Put(handles_[0], destination, meta_value); } else { @@ -1220,6 +1244,9 @@ Status RedisZSets::ZInterstore(const Slice& destination, const std::vector(final_score_members.size()))) { + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.set_count(static_cast(final_score_members.size())); batch.Put(handles_[0], destination, meta_value); } else { @@ -1360,6 +1387,9 @@ Status RedisZSets::ZRemrangebylex(const Slice& key, const Slice& min, const Slic delete iter; } if (del_cnt > 0) { + if (!parsed_zsets_meta_value.CheckModifyCount(-del_cnt)){ + return Status::InvalidArgument("zset size overflow"); + } parsed_zsets_meta_value.ModifyCount(-del_cnt); batch.Put(handles_[0], key, meta_value); *ret = del_cnt;