Fix use of time_t internally and APIs#17786
Conversation
|
I'm expecting most of the checks to fail, since this is incomplete. |
This changes internal times to use time_t. Replacement public APIs (for backwards compatibility) are introduced to allow time_t throughout the system. A new function OPENSSL_time_t_compare() is defined that accepts time_t values and offsets and can be used anywhere comparison of time is necessary.
|
I still need to update |
| size_t rawlen, hmaclen, hrrlen, ciphlen; | ||
| unsigned long tm, now; | ||
| time_t now; | ||
| unsigned long tm; |
There was a problem hiding this comment.
PACKET_get_net_4() has an unsigned long * argument, which can be either 32-bit or 64-bit depending on the system; so a temporary variable is needed. That function (and related functions) should probably be changed to always use a sized-specified (e.g. 32-bit) type. A temporary is needed regardless as time_t could be 64-bit or 32-bit.
|
#17778 contains some of the same changes. We now seem to have 3 PRs that want to do something about the Y2038 problem. I'm currently not convinced that using time_t itself is the way to go ahead. My understanding is that at least glibc requires you to set a define to change from a 32 to 64 bit time_t on platforms that started as 32 bit. We probably want to change all platforms to 64 bit at some point. But if we use time_t in our public APIs, that's an ABI change. The only solution that I currently see is that we create our own type that's guaranteed to be 64 bit in the public APIs and deprecate the others. |
Yes, this includes the changes from that PR. This is more comprehensive, adding new APIs to use The question becomes, does OpenSSL want to standardize on a time-type, or keep the APIs the current mish-mash that they are. The ASN.1 and X509 APIs are fairly consistent in their use of |
bernd-edlinger
left a comment
There was a problem hiding this comment.
I think this approach is way too complicated
| if (tm > now || (now - tm) > 600) { | ||
| now = time(NULL); | ||
| if (OPENSSL_time_t_compare(now, 0, (time_t)tm, 0) < 0 | ||
| || OPENSSL_time_t_compare(now, 0, (time_t)tm, 600) > 0) { |
There was a problem hiding this comment.
This can't work because this comparison needs to be done in 32 bit arithmetic.
There was a problem hiding this comment.
No, it doesn't; it should be done in 64-bit arithmetic, regardless of time_t size.
Can you please explain why 32-bit arithmetic needs to be used?
There was a problem hiding this comment.
The time stored in the cookie is currently a uint32_t. But we could easily change that...
There was a problem hiding this comment.
The variable tm is a long, on 64-bit system, but stores only a 32-bit value, because of this:
PACKET_get_net_4(&cookie, &tm)
The PACKET_get_net_4() function really ought to only use an int32_t* or uint32_t*, but takes a unsigned long* which is 64-bits. So, the cookie time value is put into a 64-bit value on a 64-bit system.
There was a problem hiding this comment.
Ok, the original code was likely also problematic, because on a 64-bit system only 4 bytes of the 8-byte unsigned long were filled in, and thus the 32-bit cookie time value was not sign-extended. Because it's not sign-extended, the code treats times pre-1970 as post-2038 (EDIT: on 32-bit systems).
There was a problem hiding this comment.
The time stored in the cookie is currently a
uint32_t. But we could easily change that...
Yeah, it probably should be a uint64_t, but that would make this much more complicated.
There was a problem hiding this comment.
The time is put into the cookie as:
WPACKET_put_bytes_u32(pkt, (unsigned int)time(NULL))
Past 2038, for a 64-bit time_t, when it's extracted out, it will continue to be a treated properly, because it's not sign-extended when it's extracted. This effectively gives OpenSSL's cookie an 68 extra years (until 2106).
Past 2038, for a 32-bit time_t, there are problems, as we don't know what the behavior of time(NULL) is, it could wrap, it could give -1, it could give MAX_INT.
There was a problem hiding this comment.
The value in tm is always going to be only 32-bits, regardless of sizeof(tm).
Common situations:
If sizeof(unsigned long) == sizeof(time_t) == 4 then the system can only safely handle time until 2038; as the time value put into the cookie is unknown.
If sizeof(unsigned long) == sizeof(time_t) == 8 then the code can only safely handle time until 2106. The unsigned tm is not sign-extended when converted to a time_t. The subsequent 64-bit arithmetic works.
Less common situations:
If sizeof(unsigned long) == 4 && sizeof(time_t) == 8 then the code can only safely handle time until 2106. The unsigned tm is not signed extended when converted to a time_t. The subsequent 64-bit arithmetic works.
if sizeof(unsigned long) == 8 && sizeof(time_t) == 4, then the system can only safely handle time until 2038; as the time value put into the cookie is unknown.
There was a problem hiding this comment.
The sizeof(unsigned long) == 4 && sizeof(time_t) == 8 is actually common for "recent" 32 bit architecture.
Is there a reason we can't just change tm to an uint64_t?
This approach provides a consistent mechanism for using I disagree with the complexity. Admittedly, it is "big", but OpenSSL ought to move to using proper and consistent types for time (e.g. |
FWIW: Some platforms use |
I think that if OpenSSL were to adopt it's own 64-bit time type and APIs, it would use |
It's worse. On some 64-bit platforms, |
You mean... Windows? (LLP64) In this case, we aren't dealing with |
From my testing, the defaults on z/OS and NonStop also are like that, with long being 4 bytes. So more than just Windows. |
glibc has a __time64_t. But you should always use time_t with the APIs that use a time_t. glibc sets You can change time_t to be 64 bit by using _TIME_BITS=64 (also requires _FILE_OFFSET_BITS=64). This is supported since glibc 2.34. That means that if internally we want to use a 64 bit time_t, we would need to build with -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64. But if we also have time_t in our public API, applications need to have the same defines set, or things will break. We probably want to use a fixed 64 bit type for public APIs. |
Not all platforms have |
That is what I was thinking. An openssl-specific time type (e.g. |
Yes, but Windows has just a few more deployments than those OS's. |
Only a few orders of magnitude ;) |
kaduk
left a comment
There was a problem hiding this comment.
Without getting too much into the details of the implementation, it seems like the key question for us to answer is whether we want to use time_t in our public APIs.
From a standardization perspective, time_t is clearly the right time to use, but the glibc behavior of making its ABI dependent on a preprocessor symbol, and the decision of some platforms to fix time_t at 32-bits and only offer a 64-bit time64_t muddies the waters quite a bit. On the whole, like Kurt, I'm leaning towards using an int64_t for public APIs.
It does seem clear that we should use time_t internally and convert right at the public-API boundary, and the possibility for a 64-bit time_t drives the choice of int64_t as the type used for the public API.
|
News: I just had a conversation with the |
|
@rsbeckerca that mail sounds juicy :) |
Well, hmm, that's not actually quite right. We want to use |
|
Where do we want to go with time then? The conversation has sputtered out. |
My preference is to have something like |
|
I think that a common time reference (i.e. POSIX use of seconds since Jan 1, 1970 0:00:00) is at least a good starting point, as it makes translation and API use easier. That would allow |
I agree with that. That usage should still up-convert to any current platform representation without change. Where I am going on our platform is trying to implement a 64-bit |
|
I checked with the NonStop dev team and |
|
For our internal use (observing timeouts and so) we only need a projection from the time_t to a 32-bit value. t0 < t1 := (int)((uint32_) t0 - (uint32_t) t1) < 0 that covers 68 years in the future and in the past. openssl/ssl/statem/extensions_srvr.c Lines 805 to 806 in 64eb07e since (uint32_t)(now - tm) > 600 |
|
For sake of completeness: on Windows, we can't rely on
|
Same conclusion as on NonStop and possibly z/OS. |
|
This PR is in a state where it requires action by @openssl/otc but the last update was 30 days ago |
|
This PR is in a state where it requires action by @openssl/otc but the last update was 61 days ago |
|
This PR is in a state where it requires action by @openssl/otc but the last update was 92 days ago |
|
This PR is in a state where it requires action by @openssl/otc but the last update was 123 days ago |
|
This PR is in a state where it requires action by @openssl/otc but the last update was 154 days ago |
|
Was this intended to be closed without resolution or did I miss something? |
|
I closed it as I put up a newer version that uses |


WIP: to get initial feedback on how to handle time.
This WIP PR changes internal use of time to use
time_t. Replacement public APIs (for backwards compatibility) are introduced to allow time_t through the system.A new function
OPENSSL_time_t_compare()is defined that acceptstime_tvalues and offsets and can be used anywhere comparison of time is necessary.There's still work to be done, but it's a proposal. Tests and documentation will also be updated.
Checklist