diff --git a/src/timeout.c b/src/timeout.c index 36cea7c26..7015ed979 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -164,12 +164,19 @@ void handleBlockedClientsTimeout(void) { int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) { long long tval; long double ftval; + mstime_t now = mstime(); if (unit == UNIT_SECONDS) { if (getLongDoubleFromObjectOrReply(c,object,&ftval, "timeout is not a float or out of range") != C_OK) return C_ERR; - tval = (long long) (ftval * 1000.0); + + ftval *= 1000.0; /* seconds => millisec */ + if (ftval > LLONG_MAX) { + addReplyError(c, "timeout is out of range"); + return C_ERR; + } + tval = (long long) ftval; } else { if (getLongLongFromObjectOrReply(c,object,&tval, "timeout is not an integer or out of range") != C_OK) @@ -182,7 +189,11 @@ int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int } if (tval > 0) { - tval += mstime(); + if (tval > LLONG_MAX - now) { + addReplyError(c,"timeout is out of range"); /* 'tval+now' would overflow */ + return C_ERR; + } + tval += now; } *timeout = tval; diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl index 67c36eaf4..fb204d308 100644 --- a/tests/unit/type/list.tcl +++ b/tests/unit/type/list.tcl @@ -1215,6 +1215,14 @@ foreach {pop} {BLPOP BLMPOP_LEFT} { } {foo{t} aguacate} } + test "BLPOP: timeout value out of range" { + # Timeout is parsed as float and multiplied by 1000, added mstime() + # and stored in long-long which might lead to out-of-range value. + # (Even though given timeout is smaller than LLONG_MAX, the result + # will be bigger) + assert_error "ERR *is out of range*" {r BLPOP blist1 0x7FFFFFFFFFFFFF} + } + foreach {pop} {BLPOP BRPOP BLMPOP_LEFT BLMPOP_RIGHT} { test "$pop: with single empty list argument" { set rd [redis_deferring_client] diff --git a/tests/unit/wait.tcl b/tests/unit/wait.tcl index 0f52ee15f..0ab36a0e6 100644 --- a/tests/unit/wait.tcl +++ b/tests/unit/wait.tcl @@ -18,7 +18,20 @@ start_server {} { fail "Replication not started." } } - + + test {WAIT out of range timeout (milliseconds)} { + # Timeout is parsed as milliseconds by getLongLongFromObjectOrReply(). + # Verify we get out of range message if value is behind LLONG_MAX + # (decimal value equals to 0x8000000000000000) + assert_error "*or out of range*" {$master wait 2 9223372036854775808} + + # expected to fail by later overflow condition after addition + # of mstime(). (decimal value equals to 0x7FFFFFFFFFFFFFFF) + assert_error "*timeout is out of range*" {$master wait 2 9223372036854775807} + + assert_error "*timeout is negative*" {$master wait 2 -1} + } + test {WAIT should acknowledge 1 additional copy of the data} { $master set foo 0 $master incr foo