Skip to content

Commit a87fe66

Browse files
abhijatadiholden
authored andcommitted
fix(hset_family): Fix crash on scan after expiry set (#4802)
When expiry is set on an hset its members are migrated to a string map. If a scan is performed on the hset, the code branch for string map accessed the value pointer directly by loading the 64 bit value from the object pointer. When creating a new entry in string map with TTL, we also set a bit on the stored pointer as metadata, this is set as 1 << 63. As a result when loading back the pointer we need to unset the same bit to get the correct address, this is done already in string map internals but the scan code did not do this, resulting in segfault. This change adds an AND operation to unset the TTL bit before dereferencing the pointer. Signed-off-by: Abhijat Malviya <abhijat@dragonflydb.io>
1 parent 63328b1 commit a87fe66

File tree

4 files changed

+23
-8
lines changed

4 files changed

+23
-8
lines changed

src/core/string_map.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ namespace {
2222
constexpr uint64_t kValTtlBit = 1ULL << 63;
2323
constexpr uint64_t kValMask = ~kValTtlBit;
2424

25-
inline sds GetValue(sds key) {
26-
char* valptr = key + sdslen(key) + 1;
27-
uint64_t val = absl::little_endian::Load64(valptr);
28-
return (sds)(kValMask & val);
29-
}
30-
3125
// Returns key, tagged value pair
3226
pair<sds, uint64_t> CreateEntry(string_view field, string_view value, uint32_t time_now,
3327
uint32_t ttl_sec) {
@@ -189,6 +183,12 @@ void StringMap::RandomPairs(unsigned int count, std::vector<sds>& keys, std::vec
189183
}
190184
}
191185

186+
sds StringMap::GetValue(sds key) {
187+
char* valptr = key + sdslen(key) + 1;
188+
const uint64_t val = absl::little_endian::Load64(valptr);
189+
return (sds)(kValMask & val);
190+
}
191+
192192
pair<sds, bool> StringMap::ReallocIfNeeded(void* obj, float ratio) {
193193
sds key = (sds)obj;
194194
size_t key_len = sdslen(key);

src/core/string_map.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ class StringMap : public DenseSet {
147147
void RandomPairs(unsigned int count, std::vector<sds>& keys, std::vector<sds>& vals,
148148
bool with_value);
149149

150+
static sds GetValue(sds key);
151+
150152
private:
151153
// Reallocate key and/or value if their pages are underutilized.
152154
// Returns new pointer (stays same if key utilization is enough) and if reallocation happened.

src/server/hset_family.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,7 @@ OpResult<StringVec> OpScan(const OpArgs& op_args, std::string_view key, uint64_t
320320
size_t len = sdslen(val);
321321
if (scan_op.Matches(string_view(val, len))) {
322322
res.emplace_back(val, len);
323-
val += (len + 1);
324-
val = (sds)absl::little_endian::Load64(val);
323+
val = StringMap::GetValue(val);
325324
res.emplace_back(val, sdslen(val));
326325
}
327326
};

src/server/hset_family_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,4 +507,18 @@ TEST_F(HSetFamilyTest, EmptyHashBug) {
507507
EXPECT_THAT(Run({"EXISTS", "foo"}), IntArg(0));
508508
}
509509

510+
TEST_F(HSetFamilyTest, ScanAfterExpireSet) {
511+
EXPECT_THAT(Run({"HSET", "aset", "afield", "avalue"}), IntArg(1));
512+
EXPECT_THAT(Run({"HEXPIRE", "aset", "1", "FIELDS", "1", "afield"}), IntArg(1));
513+
514+
const auto resp = Run({"HSCAN", "aset", "0", "count", "100"});
515+
EXPECT_THAT(resp, ArrLen(2));
516+
517+
const auto vec = StrArray(resp.GetVec()[1]);
518+
EXPECT_EQ(vec.size(), 2);
519+
520+
EXPECT_THAT(vec, Contains("afield").Times(1));
521+
EXPECT_THAT(vec, Contains("avalue").Times(1));
522+
}
523+
510524
} // namespace dfly

0 commit comments

Comments
 (0)