Fix time_t arithmetic after 2038#17701
Conversation
Some arithmetics using long when this type is 32 bit and time_t is 64 bit might break after 2038.
|
Would it make sense to implement our own non-floating point version of |
|
normally I would simply use time_t, but that does not work because of the frozen ABI. |
|
I'm not sure I understand. |
|
|
||
| bytes_read = 0; | ||
| finishtime = (long)time(NULL) + maxtime; | ||
| finishtime = (long)((unsigned long)time(NULL) + maxtime); |
There was a problem hiding this comment.
AFAICT the local variables finishtime and maxtime are not part of the API or ABI. Can't we change their type (and other internal variables' type) to be native time_t and only cast at the boundaries where we have to conform to existing API contracts?
There was a problem hiding this comment.
Yes, that makes sense, I am also tempted to reverse the computation and store a starttime instead of the finishtime, that needs also lots of type casts, but would also be robust if time is set back.
There was a problem hiding this comment.
I would advise using a separate PR for the "store starttime instead of finishtime" change.
consider #17658 where the internal type was changed from long to time_t in 3.0 |
|
Needs second approval. |
| /* We tolerate a cookie age of up to 10 minutes (= 60 * 10 seconds) */ | ||
| now = (unsigned long)time(NULL); | ||
| if (tm > now || (now - tm) > 600) { | ||
| if ((uint32_t)(now - tm) > 600) { |
There was a problem hiding this comment.
Why is this cast needed? now - tm is already evaluated in unsigned long, and I don't see that casting the result of the subtraction is needed in order to compare against 600.
There was a problem hiding this comment.
Internally, we should really be using time_t and not casting to unsigned long or uint32_t unless it's being returned from a public API.
consider #17658 where the internal type was changed from long to time_t in 3.0 while the public API kept long. That is just plain broken.
Yes and no. The public API is certainly broken; the internal use of time_t isn't. I don't think there was a will to change public APIs to use time_t in the 3.0 release, due to backwards compatibility concerns. Although we did change the options to use uint64_t...
There was a problem hiding this comment.
Examples such as #8687 updated the types for SSL_SESSION to be time_t; as an example of how to deal with this.
There was a problem hiding this comment.
Why is this cast needed?
now - tmis already evaluated inunsigned long, and I don't see that casting the result of the subtraction is needed in order to compare against 600.
although tm is of type unsigned long it is only a 4 byte value, see here:
openssl/ssl/statem/extensions_srvr.c
Line 796 in d2d2401
and here:
openssl/include/internal/packet.h
Lines 217 to 228 in d2d2401
Well, it will not break in 2038, but in 2106, but it will eventually break.
There was a problem hiding this comment.
Examples such as #8687 updated the types for
SSL_SESSIONto betime_t; as an example of how to deal with this.
This would only be okay if the API changes as well. If the API cannot change, this simply breaks the API.
When the API cannot change, we should make the internal time representation a cyclic time model.
There was a problem hiding this comment.
although tm is of type unsigned long it is only a 4 byte value, see here:
Yes, I saw that before I commented.
Please spell out a plausible scenario where the result of evaluating (uint32_t)(now - tm) > 600 and the result of evaluating (now - tm) > 600 will be different. We're measuring the delay between when we issued the cookie and when we're processing the copy sent back to us. If our clock went backwards, both expressions will produce a large value for the LHS since the nominally negative value "wraps around" in unsigned arithmetic, and it seems physically implausible for us to wait more than 2^32 seconds between issuing and receiving the cookie.
I don't see a part of this particular codeline that is using absolute time and thus subject to 2038 or 2106 issues -- I only see unsigned arithmetic that's comparing time differences against a duration.
There was a problem hiding this comment.
Oh, I really thought that would be obvious:
let unsigned long be 64 byte long, and time_t be 64 byte long as well,
and the year after 2106.
Then (now - tm) will always be > 2^32.
QED.
There was a problem hiding this comment.
Well, it will not break in 2038, but in 2106, but it will eventually break.
This is a flawed assumption. 32-bit time_t is signed, it does not support any date beyond 2038, nor before 1902. you cannot cast a signed time_t to an unsigned type and assume it will work beyond 2038 (as you are ignoring dates before 1970). Most software in a 32-bit time_t system will be completely broken in 2038.
There was a problem hiding this comment.
No, I am of course talking about systems using 64 bit time_t, and this silly code will break, because long is a compiler dependent thing.
There was a problem hiding this comment.
Oh, I really thought that would be obvious:
let unsigned long be 64 byte long, and time_t be 64 byte long as well,
and the year after 2106.
I suppose it probably would have been obvious if I had been primed to think about 2106. But the PR title and the commit's commit message only mentioned 2038...
|
I put an alternative up in #17778 that uses time_t internally for |
|
I think the version in #17778 is clearer but I'm okay approving either. |
Possibly a function to compare two I'd suspect that one of the offsets would always be 0, as in the case of checking for a finish time: Use this (or similar) for all time calculations, and a lot of issues go away. I did something similar in #8687 |
|
Alternative proposal: #17786 (it's a WIP, and could subsume this work). |
|
24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually. |
|
Let's see what the alternative ends up, it looks most promising IMO. |
|
Perhaps we should fork off an issue for discussion of what we are actually trying to fix.
|
I tried to fix one of these types of situations; but because of 64-bit longs, it didn't work. The function extracted 4 bytes, so one would think |
That thought had come to me too. Possibly use an |
|
The approval is stale, this needs to be rebased or abandoned. |
|
@bernd-edlinger please rebase this or close if you are no longer interested in this PR. |
|
This PR is waiting for the creator to make requested changes but it has not been updated for 30 days. If you have made changes or commented to the reviewer please make sure you re-request a review (see icon in the 'reviewers' section). |
|
This PR is waiting for the creator to make requested changes but it has not been updated for 61 days. If you have made changes or commented to the reviewer please make sure you re-request a review (see icon in the 'reviewers' section). |
|
This PR has been closed. It was waiting for the creator to make requested changes but it has not been updated for 90 days. |
Some arithmetics using long when this type is 32
bit and time_t is 64 bit might break after 2038.