Projects/Timestamps after 2038
This project will make MIT krb5 timestamps work on platforms with 64-bit time_t for times after January 2038, up through January 2106, by repurposing the negative number range of the krb5_timestamp type.
krb5.h defines the type krb5_timestamp as follows:
typedef krb5_int32 krb5_timestamp;
The range of this type extends to time values up through 2038-01-19 03:14:07 UTC. On most 32-bit Unix platforms, the native time_t type also has this limitation, and numerous applications other than MIT krb5 will fail to work after the year 2037. On platforms with a 64-bit time_t type, the limited range of krb5_timestamp will only cause MIT krb5 to fail after 2037.
The krb5_timestamp type is used in numerous libkrb5 structures and function signatures. Changing the size of the type would be an incompatible ABI change, which would pose a disruption to downstream packagers. Changing the type to unsigned would have a more subtle impact on the API, but could still create problems for existing code.
In the GSSAPI, timestamps are represented using OM_uint32.
Kerberos does not generally need to represent time values before the year 1970. Therefore, negative krb5_timestamp values can be taken to represent times between 2038 and 2106, as if the type were unsigned. To accomplish this, we can define a conversion function from krb5_timestamp to time_t as follows:
- If time_t is 32-bit, the conversion is the identity function.
- If the value is nonnegative, the conversion is the identity function.
- Otherwise, the conversion adds 2^32 to the value. (We might need to preserve the value -1 rather than converting it to 2^32-1, but the need for this is unclear.)
If we do not need to preserve the value -1, this conversion is as simple as (time_t)(uint32_t)timestamp, whether time_t is a 32-bit or 64-bit integer.
The inverse of this conversion is trivial; simply casting from time_t to krb5_timestamp will have the desired behavior, whether or not the value -1 needs to be preserved.
C language considerations
Conversion to a signed type from a value outside of the range of the signed type is specified in C99 section 22.214.171.124 as "the result is implementation-defined or an implementation-defined signal is raised". C99 section 3.4.1 defines implementation-defined behavior as "unspecified behavior where each implementation documents how the choice is made"; the result must be consistent for the same operands, and the compiler cannot simply assume that implementation-defined behavior never happens. We check in configure.in that conversion to signed types preserves the twos-complement bit representation and does not crash the program; therefore, we can safely rely (for example) on implicit conversions from time_t to krb5_timestamp to generate the appropriate negative value for times between years 2038 and 2106.
Arithmetic operations on a signed type which overflow or underflow are undefined by the C standard. Compilers frequently take advantage of the assumption that undefined behavior cannot occur when optimizing code, so we should try to avoid undefined behavior when operating on krb5_timestamp values. Casting from krb5_timestamp to uint32_t before adding or subtracting an offset should be an adequate workaround.
Conversions to an unsigned integer type are well-specified (C99 section 126.96.36.199), as are arithmetic operations on unsigned types (C99 section 6.2.5). Values that cannot be represented in the range of the unsigned type are reduced modulo that range.
- Is -1 used as a distinguished timestamp value anywhere in the code? If so, we must be careful to preserve the value -1 when converting from krb5_timestamp to time_t.