Skip to content

Commit bdc12cd

Browse files
authored
fix: stack overflow in hincrbyfloat (#5835)
* fix: stack overflow in hincrbyfloat * refactor: address comments * fix: process errors more accurately for incrbyfloat
1 parent f36ad68 commit bdc12cd

File tree

11 files changed

+40
-39
lines changed

11 files changed

+40
-39
lines changed

src/facade/error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ inline constexpr char kUndeclaredKeyErr[] = "script tried accessing undeclared k
3838
inline constexpr char kInvalidDumpValueErr[] = "DUMP payload version or checksum are wrong";
3939
inline constexpr char kInvalidJsonPathErr[] = "invalid JSON path";
4040
inline constexpr char kJsonParseError[] = "failed to parse JSON";
41+
inline constexpr char kNanOrInfDuringIncr[] = "increment would produce NaN or Infinity";
4142
inline constexpr char kCrossSlotError[] = "-CROSSSLOT Keys in request don't hash to the same slot";
4243

4344
inline constexpr char kSyntaxErrType[] = "syntax_error";

src/facade/op_status.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ std::string_view StatusToMsg(OpStatus status) {
4040
return kInvalidJsonPathErr;
4141
case OpStatus::INVALID_JSON:
4242
return kJsonParseError;
43+
case OpStatus::NAN_OR_INF_DURING_INCR:
44+
return kNanOrInfDuringIncr;
4345
default:
4446
LOG(ERROR) << "Unsupported status " << status;
4547
return "Internal error";

src/facade/op_status.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ enum class OpStatus : uint16_t {
3333
MEMBER_NOTFOUND,
3434
INVALID_JSON_PATH,
3535
INVALID_JSON,
36-
IO_ERROR
36+
IO_ERROR,
37+
NAN_OR_INF_DURING_INCR,
3738
};
3839

3940
class OpResultBase {

src/redis/util.c

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -207,33 +207,3 @@ int string2ll(const char *s, size_t slen, long long *value) {
207207
}
208208
return 1;
209209
}
210-
211-
/* Convert a string into a double. Returns 1 if the string could be parsed
212-
* into a (non-overflowing) double, 0 otherwise. The value will be set to
213-
* the parsed value when appropriate.
214-
*
215-
* Note that this function demands that the string strictly represents
216-
* a double: no spaces or other characters before or after the string
217-
* representing the number are accepted. */
218-
int string2ld(const char *s, size_t slen, long double *dp) {
219-
char buf[MAX_LONG_DOUBLE_CHARS];
220-
long double value;
221-
char *eptr;
222-
223-
if (slen == 0 || slen >= sizeof(buf)) return 0;
224-
memcpy(buf,s,slen);
225-
buf[slen] = '\0';
226-
227-
errno = 0;
228-
value = strtold(buf, &eptr);
229-
if (isspace(buf[0]) || eptr[0] != '\0' ||
230-
(size_t)(eptr-buf) != slen ||
231-
(errno == ERANGE &&
232-
(value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
233-
errno == EINVAL ||
234-
isnan(value))
235-
return 0;
236-
237-
if (dp) *dp = value;
238-
return 1;
239-
}

src/redis/util.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747

4848
int ll2string(char *s, size_t len, long long value);
4949
int string2ll(const char *s, size_t slen, long long *value);
50-
int string2ld(const char *s, size_t slen, long double *dp);
5150

5251
#define LOG_MAX_LEN 1024 /* Default maximum length of syslog messages.*/
5352

src/server/dfly_main.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ namespace {
102102
// need additional 8-16 bytes for their internal structures, thus over reserving additional
103103
// memory pages if using round sizes.
104104
#ifdef NDEBUG
105-
constexpr size_t kFiberDefaultStackSize = 36_KB - 16;
105+
constexpr size_t kFiberDefaultStackSize = 32_KB - 16;
106106
#else
107-
// Increase stack size for debug builds.
108-
constexpr size_t kFiberDefaultStackSize = 40_KB - 16;
107+
// We leave the same stack size for debug mode, to find out the possible stack overflow bugs.
108+
constexpr size_t kFiberDefaultStackSize = 32_KB - 16;
109109
#endif
110110

111111
enum class TermColor { kDefault, kRed, kGreen, kYellow };

src/server/hset_family.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,16 @@ size_t HMapLength(const DbContext& db_cntx, const CompactObj& co) {
131131
OpStatus IncrementValue(optional<string_view> prev_val, IncrByParam* param) {
132132
if (holds_alternative<double>(*param)) {
133133
double incr = get<double>(*param);
134-
long double value = 0;
134+
double value = 0;
135135

136136
if (prev_val) {
137-
if (!string2ld(prev_val->data(), prev_val->size(), &value)) {
137+
if (!ParseDouble(*prev_val, &value)) {
138138
return OpStatus::INVALID_VALUE;
139139
}
140140
}
141141
value += incr;
142142
if (isnan(value) || isinf(value)) {
143-
return OpStatus::INVALID_FLOAT;
143+
return OpStatus::NAN_OR_INF_DURING_INCR;
144144
}
145145

146146
param->emplace<double>(value);

src/server/hset_family_test.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@ TEST_F(HSetFamilyTest, HincrbyFloat) {
197197
}
198198
}
199199

200+
TEST_F(HSetFamilyTest, HincrbyFloatCornerCases) {
201+
Run({"hset", "k", "mhv", "-1.8E+308", "phv", "1.8E+308", "nd", "-+-inf", "+inf", "+inf", "nan",
202+
"nan", "-inf", "-inf"});
203+
// we don't support long doubles, so in all next cases we should return errors
204+
EXPECT_THAT(Run({"hincrbyfloat", "k", "mhv", "-1"}), ErrArg("ERR hash value is not a float"));
205+
EXPECT_THAT(Run({"hincrbyfloat", "k", "phv", "1"}), ErrArg("ERR hash value is not a float"));
206+
EXPECT_THAT(Run({"hincrbyfloat", "k", "nd", "1"}), ErrArg("ERR hash value is not a float"));
207+
EXPECT_THAT(Run({"hincrbyfloat", "k", "+inf", "1"}),
208+
ErrArg("increment would produce NaN or Infinity"));
209+
EXPECT_THAT(Run({"hincrbyfloat", "k", "nan", "1"}), ErrArg("ERR hash value is not a float"));
210+
EXPECT_THAT(Run({"hincrbyfloat", "k", "-inf", "1"}),
211+
ErrArg("increment would produce NaN or Infinity"));
212+
}
213+
200214
TEST_F(HSetFamilyTest, HRandFloat) {
201215
Run({"HSET", "k", "1", "2"});
202216

src/server/string_family.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ OpResult<double> OpIncrFloat(const OpArgs& op_args, string_view key, double val)
269269
base += val;
270270

271271
if (isnan(base) || isinf(base)) {
272-
return OpStatus::INVALID_FLOAT;
272+
return OpStatus::NAN_OR_INF_DURING_INCR;
273273
}
274274

275275
char* str = RedisReplyBuilder::FormatDouble(base, buf, sizeof(buf));

src/server/string_family_test.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,10 @@ TEST_F(StringFamilyTest, IncrByFloat) {
519519
auto resp = Run({"INCRBYFLOAT", "nonum", "1.0"});
520520
EXPECT_THAT(resp, ErrArg("not a valid float"));
521521

522+
Run({"SET", "inf", "+inf"});
523+
resp = Run({"INCRBYFLOAT", "inf", "1.0"});
524+
EXPECT_THAT(resp, ErrArg("increment would produce NaN or Infinity"));
525+
522526
Run({"SET", "nonum", "11 "});
523527
resp = Run({"INCRBYFLOAT", "nonum", "1.0"});
524528
EXPECT_THAT(resp, ErrArg("not a valid float"));

0 commit comments

Comments
 (0)