Skip to content

Commit 09424b2

Browse files
YuCai18caiyu
andauthored
Fix the error bugs in getrange and setrange in pika 3.5 version (#3106)
Co-authored-by: caiyu <[email protected]>
1 parent a9b914a commit 09424b2

File tree

6 files changed

+78
-1
lines changed

6 files changed

+78
-1
lines changed

conf/pika.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ db-path : ./db/
8686
# Supported Units [K|M|G], write-buffer-size default unit is in [bytes].
8787
write-buffer-size : 256M
8888

89+
# The maximum size of a single bulk string in Pika protocol.
90+
# This value is used to limit the size of a single bulk string in Pika protocol.
91+
# The default value is 512M.
92+
proto-max-bulk-len : 512M
93+
8994
# The size of one block in arena memory allocation.
9095
# If <= 0, a proper value is automatically calculated.
9196
# (usually 1/8 of writer-buffer-size, rounded up to a multiple of 4KB)

include/pika_conf.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ class PikaConf : public pstd::BaseConf {
124124
std::shared_lock l(rwlock_);
125125
return write_buffer_size_;
126126
}
127+
int64_t proto_max_bulk_len() {
128+
std::shared_lock l(rwlock_);
129+
return proto_max_bulk_len_;
130+
}
127131
int64_t arena_block_size() {
128132
std::shared_lock l(rwlock_);
129133
return arena_block_size_;
@@ -745,6 +749,11 @@ class PikaConf : public pstd::BaseConf {
745749
rsync_timeout_ms_.store(value);
746750
}
747751

752+
void SetProtoMaxBulkLen(const int64_t value) {
753+
std::lock_guard l(rwlock_);
754+
TryPushDiffCommands("proto-max-bulk-len", std::to_string(value));
755+
proto_max_bulk_len_ = value;
756+
}
748757
int RocksDBPerfLevel() const {
749758
return rocksdb_perf_level_.load();
750759
}
@@ -907,6 +916,7 @@ class PikaConf : public pstd::BaseConf {
907916
int64_t least_free_disk_to_resume_ = 268435456; // 256 MB
908917
double min_check_resume_ratio_ = 0.7;
909918
int64_t write_buffer_size_ = 0;
919+
int64_t proto_max_bulk_len_ = 0;
910920
int64_t arena_block_size_ = 0;
911921
int64_t slotmigrate_thread_num_ = 0;
912922
int64_t thread_migrate_keys_num_ = 0;

src/pika_cache.cc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,39 @@ Status PikaCache::Appendxx(std::string& key, std::string &value) {
440440
return Status::NotFound("key not exist");
441441
}
442442

443+
/*
444+
Added boundary checks for start and end parameters to the PikaCache::GetRange function,
445+
and used the full_value variable to store the actual length of string type,
446+
avoiding excessive memory allocation by sdsnewlen.
447+
*/
443448
Status PikaCache::GetRange(std::string& key, int64_t start, int64_t end, std::string *value) {
444449
int cache_index = CacheIndex(key);
445450
std::lock_guard lm(*cache_mutexs_[cache_index]);
446-
return caches_[cache_index]->GetRange(key, start, end, value);
451+
std::string full_value;
452+
auto s = caches_[cache_index]->Get(key, &full_value);
453+
if (!s.ok()) {
454+
return s;
455+
}
456+
int64_t strlen = full_value.size();
457+
458+
if (start < 0) {
459+
start = strlen + start;
460+
}
461+
if (end < 0) {
462+
end = strlen + end;
463+
}
464+
465+
if (start < 0) start = 0;
466+
if (end < 0) end = 0;
467+
if (end >= strlen) end = strlen - 1;
468+
469+
if (start > end || strlen == 0) {
470+
value->clear();
471+
return Status::OK();
472+
}
473+
474+
*value = full_value.substr(start, end - start + 1);
475+
return Status::OK();
447476
}
448477

449478
Status PikaCache::SetRangeIfKeyExist(std::string& key, int64_t start, std::string &value) {

src/pika_conf.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ int PikaConf::Load() {
323323
write_buffer_size_ = 268435456; // 256Mb
324324
}
325325

326+
GetConfInt64Human("proto-max-bulk-len", &proto_max_bulk_len_);
327+
if (proto_max_bulk_len_ <= 0) {
328+
proto_max_bulk_len_ = 512 * 1024 * 1024; // 512MB
329+
}
326330
// arena_block_size
327331
GetConfInt64Human("arena-block-size", &arena_block_size_);
328332
if (arena_block_size_ <= 0) {

src/pika_kv.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,17 @@ void SetrangeCmd::DoInitial() {
12151215
return;
12161216
}
12171217
value_ = argv_[3];
1218+
// Read the proto-max-bulk-len parameter settings in the pika configuration file pika_conf
1219+
const int64_t PROTO_MAX_BULK_LEN = g_pika_conf->proto_max_bulk_len();
1220+
//Handle the overflow issue of offset_
1221+
if (offset_ < 0) {
1222+
res_.SetRes(CmdRes::kInvalidInt, "offset is out of range");
1223+
return;
1224+
}
1225+
if (offset_ > PROTO_MAX_BULK_LEN - static_cast<int64_t>(value_.size())) {
1226+
res_.SetRes(CmdRes::kErrOther, "string exceeds maximum allowed size (proto-max-bulk-len)");
1227+
return;
1228+
}
12181229
}
12191230

12201231
void SetrangeCmd::Do() {

tests/integration/string_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,24 @@ var _ = Describe("String Commands", func() {
300300
Expect(getRange.Val()).To(Equal("string"))
301301
})
302302

303+
//Caiyu's test cases for GETRANGE and SETRANGE to fix bug #3092.
304+
It("should not crash on huge GETRANGE", func() {
305+
set := client.Set(ctx, "key1", "abc", 0)
306+
Expect(set.Err()).NotTo(HaveOccurred())
307+
Expect(set.Val()).To(Equal("OK"))
308+
getRange1 := client.GetRange(ctx, "key1", 1, 4294967296)
309+
Expect(getRange1.Val()).To(Equal("bc"))
310+
getRange2 := client.GetRange(ctx, "key1", 1, 4294967296)
311+
Expect(getRange2.Val()).To(Equal("bc"))
312+
})
313+
It("should not crash on huge SETRANGE", func() {
314+
set := client.Set(ctx, "key1", "abc", 0)
315+
Expect(set.Err()).NotTo(HaveOccurred())
316+
Expect(set.Val()).To(Equal("OK"))
317+
setRange := client.SetRange(ctx, "key1", 9223372036854775757, "value2")
318+
Expect(setRange.Err()).To(HaveOccurred())
319+
})
320+
303321
It("should GetSet", func() {
304322
incr := client.Incr(ctx, "key")
305323
Expect(incr.Err()).NotTo(HaveOccurred())

0 commit comments

Comments
 (0)