EXPIRE, EXPIREAT, SETEX, GETEX: Return error when expire time overflows#8287
EXPIRE, EXPIREAT, SETEX, GETEX: Return error when expire time overflows#8287oranagra merged 9 commits intoredis:unstablefrom
Conversation
Signed-off-by: Gnanesh <[email protected]>
befc540 to
1750a16
Compare
src/expire.c
Outdated
| /* | ||
| * If the provided expiry from input was negative, and after | ||
| * unit conversion and adding `basetime`, if the key was still | ||
| * not expired, there is an integer overflow. | ||
| */ | ||
| if (is_expire_input_negative && when > 0) { | ||
| addReplyErrorFormat(c, "invalid expire time in %s", c->cmd->name); | ||
| return; | ||
| } | ||
| setExpire(c,c->db,key,when); |
There was a problem hiding this comment.
Before:
127.0.0.1:6379> SET A B
OK
127.0.0.1:6379> EXPIRE A -9999999999999999
(integer) 1
127.0.0.1:6379> TTL A
(integer) 8446744073709551
127.0.0.1:6379>
After:
127.0.0.1:6379> SET A B
OK
127.0.0.1:6379> EXPIRE A -9999999999999999
(error) ERR invalid expire time in expire
127.0.0.1:6379> GET A
"B"
127.0.0.1:6379>
oranagra
left a comment
There was a problem hiding this comment.
i think this is looks a bit convoluted.
for setGenericCommand, without a big comment it's hard to understand why we're repeating that check twice.
for expireGenericCommand it's odd to see an input validation check at the bottom of the functoin.
how about moving the one and only check to be above the multiplication, and using this condition:
if (when <= 0 || (unit == UNIT_SECONDS && when * 1000 < 0)) {|
btw, it may be a nice idea to add tests for this, so that we can catch any future regression. |
b474c0f to
63280e7
Compare
I have tried the suggestion, but the condition behaves a little odd. The compiler smartly make the right hand I'm afraid of the future problems due to different compiler behaviours though. You can check the same in the below video: conv.mp4
Actually, thats kind of important. For a positive to negative number overflow, all we have to do is try to convert it to seconds by ( For a negative to positive number overflow, we do the necessary unit conversions, and we need to check whether int is_expire_input_negative = when < 0;
if (checkAlreadyExpired(when)) {
} else {
if (is_expire_input_negative && when > 0) {
printf("negavite to positive overflow");
}
}Moving the condition won't work since we completely depend on And BTW, the tests passes on my machine but fails on CI even though they're simple. Will debug and modify the test case. |
ea31f2e to
7001e2d
Compare
|
@GnaneshKunal if the compiler doesn't detect the possibility for overflow in that if (when <= 0 || (unit == UNIT_SECONDS && when >= LLONG_MAX / 1000))this will let us keep the input check at the input and avoid repeating the code that responds with error. |
That works perfectly. |
src/expire.c
Outdated
There was a problem hiding this comment.
let's convert that one too..
i.e. move the input check to share the same early exit with the other one at the top.
25039f9 to
c25f2e4
Compare
src/expire.c
Outdated
| if (when <= 0 || (unit == UNIT_SECONDS && when >= LLONG_MAX / 1000)) { | ||
| addReplyErrorFormat(c, "invalid expire time in %s", c->cmd->name); | ||
| return; | ||
| } |
There was a problem hiding this comment.
this check needs to move to before the adjustment above.
There was a problem hiding this comment.
Before the unit conversion, right?
There was a problem hiding this comment.
when <= 0 makes EXPIRE to not take negative inputs at all.
fdb3d60 to
c25f2e4
Compare
|
@GnaneshKunal thank you.. the current version looks much better than the earlier ones. one concern about absolute timesIn theory when i suppose that considering we're discussing wall clock here (not an arbitrary date record in a database), a negative absolute time (1969) is not a real concern. but i'd like to validate that with @yossigo. in theory this change can break some application (return an error for a command that was previously valid), but i think it is more likely that he'll help someone find a bug in his code rather than break an innocent app. meanwhile, i'm marking this a |
|
more details about the negative absolute expire time:
|
Actually for EXPIRE, negative value is still valid to some extend. For example, Example:
This is true for
Instead of removing if ((basetime != 0 && when <= 0) || (unit == UNIT_SECONDS && when >= LLONG_MAX / 1000)) |
|
ohh, i was sure you took my advise in #8287 (comment) and moved the check to look at the raw input from the user (not after basetime is added).
although this makes sense (allow absolute time before 1970, and reject just negative relative time), i suggested it too, i'm now afraid that this can cause a breaking change to some app. bottom line: if we're not sure, we can wait to see what Yossi thinks. |
Tried it but few non-expire test cases were failing. Looks like some tests cases for MASTER and SLAVE consistency. So ignored the change. Here's the complete trace:
Okay.
Sure. |
|
i thought about that it was complicated so i decided to let it go. |
All good. Tested it and works fine. |
|
@redis/core-team i'm not entirely sure this is a "major decision" but better safe than sorry, so please have a look.
|
|
btw, running the new tests with the old version gives this: |
…ws (redis#8287) Respond with error if expire time overflows from positive to negative of vice versa. * `SETEX`, `SET EX`, `GETEX` etc would have already error on negative value, but now they would also error on overflows (i.e. when the input was positive but after the manipulation it becomes negative, which would have passed before) * `EXPIRE` and `EXPIREAT` was ok taking negative values (would implicitly delete the key), we keep that, but we do error if the user provided a value that changes sign when manipulated (except the case of changing sign when `basetime` is added) Signed-off-by: Gnanesh <[email protected]> Co-authored-by: Oran Agra <[email protected]>
|
was tipped by @itamarhaber that this PR closed #86 (the oldest issue number I've seen getting closed). |
…ws (redis#8287) Respond with error if expire time overflows from positive to negative of vice versa. * `SETEX`, `SET EX`, `GETEX` etc would have already error on negative value, but now they would also error on overflows (i.e. when the input was positive but after the manipulation it becomes negative, which would have passed before) * `EXPIRE` and `EXPIREAT` was ok taking negative values (would implicitly delete the key), we keep that, but we do error if the user provided a value that changes sign when manipulated (except the case of changing sign when `basetime` is added) Signed-off-by: Gnanesh <[email protected]> Co-authored-by: Oran Agra <[email protected]>
In redis#8287, some overflow checks have been added. But when `when *= 1000` overflows, it will become a positive number. And the check not able to catch it. The key will be added with a short expiration time and will deleted a few seconds later. In redis#9601, will check the overflow after `*=` and return an error first, and avoiding this situation. In this commit, added some tests to cover those code paths. Found it in redis#9825, and close it.
In #8287, some overflow checks have been added. But when `when *= 1000` overflows, it will become a positive number. And the check not able to catch it. The key will be added with a short expiration time and will deleted a few seconds later. In #9601, will check the overflow after `*=` and return an error first, and avoiding this situation. In this commit, added some tests to cover those code paths. Found it in #9825, and close it.
In redis#8287, some overflow checks have been added. But when `when *= 1000` overflows, it will become a positive number. And the check not able to catch it. The key will be added with a short expiration time and will deleted a few seconds later. In redis#9601, will check the overflow after `*=` and return an error first, and avoiding this situation. In this commit, added some tests to cover those code paths. Found it in redis#9825, and close it.
In #8287, some overflow checks have been added. But when `when *= 1000` overflows, it will become a positive number. And the check not able to catch it. The key will be added with a short expiration time and will deleted a few seconds later. In #9601, will check the overflow after `*=` and return an error first, and avoiding this situation. In this commit, added some tests to cover those code paths. Found it in #9825, and close it. (cherry picked from commit 9273d09)
TL:DR: Respond with error if expire time overflows from positive to negative of vice versa.
SETEX,SET EX,GETEXetc would have already error on negative value,but now they would also error on overflows (i.e. when the input was positive but
after the manipulation it becomes negative, which would have passed before)
EXPIREandEXPIREATwas ok taking negative values (would implicitly deletethe key), we keep that, but we do error if the user provided a value that changes
sign when manipulated (except the case of changing sign when
basetimeis added)If we set the expiry of a key to 10^15, the key is automatically expired due to an overflow while trying to convert the value from seconds to milliseconds.
Expire time overflows (GDB):
Earlier:
Now: