DRAFT: Y2038 Proofness Design


History

Revision 24 was reviewed on the libc-alpha mailing list as 1st draft: https://www.sourceware.org/ml/libc-alpha/2015-10/msg00893.html

Revision 55 was reviewed on the libc-alpha mailing list as 2nd draft: https://www.sourceware.org/ml/libc-alpha/2016-01/msg00832.html

Scope

The intent of this page is to serve as a central point for describing the Y2038 proofness design.

Y2038 proofness means that application calls to glibc-provided function should never return wrong results when UTC times outside -231..231-1 seconds from the Unix Epoch are involved.

Useful Definitions

Goals

Constraints

There are a number of constraints which dictate the direction of the design. They are either definite or debatable. Debatable design preclusions should be finalized before this design document leaves DRAFT.

Definite

Debatable

This sounds like something totally unrelated, but the underlying idea is to not multiply the possible combinations.

IOW, this design does not aim at trying to fix existing 32-bit time support at all; any fix to the Y2038 problem will be within new 64-bit time support.

API changes

This sections describes the API changes required for supporting Y2038 in GLIBC.

This section lists all time-related API symbols based on section 21 of the glibc manual (https://www.gnu.org/software/libc/manual/html_node/Date-and-Time.html#Date-and-Time) and indicates which ones are Y2038-unsafe and which ones are Y2038-safe.

* A "Y2038-unsafe" symbol is one which, in 32-bit builds, might fail to handle calendar times beyond Y2038; whether it actually fails or succeeds will be determined by analyzing the implementation.

* A "Y2038-safe" symbol is one which, even in a 32-bit build, is definitely not susceptible to Y2038-related bugs.

Function

Y2038-unsafe?

time_t

YES, as time_t is 32-bit signed on 32-bit builds

double difftime (time_t time1, time_t time0)

YES, as time_t is Y2038-unsafe

struct timeval { time_t tv_sec; long int tv_usec; } 

YES, as time_t is Y2038-unsafe

struct timespec { time_t tv_sec; long int tv_nsec; } 

YES, as time_t is Y2038-unsafe

clock_t clock(void)

No, return value is not clock time in seconds since the epoch but CPU time in clock ticks

struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; } 

No, as all members are Y2038-safe

clock_t times(struct tms *buffer)

No, as struct tms is Y2038-safe

time_t time (time_t *result)

YES, as time_t is Y2038-unsafe

int stime (const time_t *newtime)

YES, as time_t is Y2038-unsafe

struct timezone { int tz_minuteswest; int tz_dsttime; } 

No

struct timeval

YES, as struct timeval depends on time_t which is Y2038-unsafe

int gettimeofday (struct timeval *tp, struct timezone *tzp)

YES, as struct timeval is Y2038-unsafe

int settimeofday (const struct timeval *tp, const struct timezone *tzp)

YES, as struct timeval is Y2038-unsafe

int adjtime (const struct timeval *delta, struct timeval *olddelta)

YES, as struct timeval is Y2038-unsafe

struct timex

YES, as struct timex depends on struct timeval which is Y0238-unsafe

int adjtimex (struct timex *timex)

YES, as struct timex is Y0238-unsafe

struct tm { int tm_sec; int tm_min; ... } 

No, as all members are Y2038-safe

struct tm * localtime (const time_t *time)

YES, as time_t is Y2038-unsafe

struct tm * localtime_r (const time_t *time, struct tm *resultp)

YES, as time_t is Y2038-unsafe

struct tm * gmtime (const time_t *time)

YES, as time_t is Y2038-unsafe

struct tm * gmtime_r (const time_t *time, struct tm *resultp)

YES, as time_t is Y2038-unsafe

time_t mktime (struct tm *brokentime)

YES, as time_t is Y2038-unsafe

time_t timelocal (struct tm *brokentime)

YES, as time_t is Y2038-unsafe

time_t timegm (struct tm *brokentime)

YES, as time_t is Y2038-unsafe

struct ntptimeval { struct timeval time; long int maxerror; long int esterror; } 

YES, as struct timeval is Y2038-unsafe

struct ntptimeval

YES, as struct ntptimeval depends on struct timeval which is Y2038-unsafe

ntp_gettime (struct ntptimeval *tptr)

YES, as struct ntptimeval is Y2038-unsafe

struct timex { ... struct timeval time; ... } 

YES, as struct timeval time is Y2038-unsafe

int ntp_adjtime (struct timex *tptr)

YES, as struct timex is Y2038-unsafe

char * asctime (const struct tm *brokentime)

No, as struct tm is Y2038-safe

char * asctime_r (const struct tm *brokentime, char *buffer)

No, as struct tm is Y2038-safe

char * ctime (const time_t *time)

YES, as time_t is Y2038-unsafe

char * ctime_r (const time_t *time, char *buffer)

YES, as time_t is Y2038-unsafe

size_t strftime (char *s, size_t size, const char *template, const struct tm *brokentime)

No, as struct tm is Y2038-safe

size_t wcsftime (wchar_t *s, size_t size, const wchar_t *template, const struct tm *brokentime)

No, as struct tm is Y2038-safe

char * strptime (const char *s, const char *fmt, struct tm *tp)

No, as struct tm is Y2038-safe

struct tm * getdate (const char *string)

No, as struct tm is Y2038-safe

int getdate_r (const char *string, struct tm *tp)

No, as struct tm is Y2038-safe

struct itimerval

YES, as struct timeval depends on struct timeval which is Y2038-unsafe

int setitimer (int which, const struct itimerval *new, struct itimerval *old)

YES, as struct itimerval is Y2038-unsafe

int getitimer (int which, struct itimerval *old)

YES as struct itimerval is Y2038-unsafe

unsigned int alarm (unsigned int seconds)

TBC depending on implementation

unsigned int sleep (unsigned int seconds)

TBC depending on implementation

int nanosleep (const struct timespec *requested_time, struct timespec *remaining)

YES, as struct timespec is Y2038-unsafe

Other GLIBC APIs

Some other areas of the GLIB API use time_t directly or as a member of struct timeval and struct timespec, for instance struct stat which is used in the stat() function. The following table lists these.

Function

Y2038-unsafe?

Section 13.8 - Waiting for Input or Output

int select (... struct timeval *timeout)

YES, as struct timeval is Y2038-unsafe

Section 14.9.2 - Reading the Attributes of a File

struct stat

YES, as struct stat depends on struct time_t which is Y2038-unsafe

int stat (const char *filename, struct stat *buf)

YES, as struct stat is Y2038-unsafe

int fstat (int filedes, struct stat *buf)

YES, as struct stat is Y2038-unsafe

int lstat (const char *filename, struct stat *buf)

YES, as struct stat is Y2038-unsafe

Section 14.9.9 - File times

struct utimbuf { time_t actime; time_t modtime; } 

YES, as time_t is Y2038-unsafe

int utime (const char *filename, const struct utimbuf *times)

YES, as struct utimbuf is Y2038-unsafe

int utimes (const char *filename, const struct timeval tvp[2])

YES, as struct timeval is Y2038-unsafe

int lutimes (const char *filename, const struct timeval tvp[2])

YES, as struct timeval is Y2038-unsafe

int futimes (int fd, const struct timeval tvp[2])

YES, as struct timeval is Y2038-unsafe

Section 22.1 - Resource Usage

struct rusage { struct timeval ru_utime; struct timeval ru_stime; ... } 

YES, as struct_timeval is Y2038-unsafe

int getrusage (int processes, struct rusage *rusage)

YES, as struct_rusage is Y2038-unsafe

struct vtimes { struct timeval vm_utime; struct timeval vm_stime; ... } 

YES, as struct_timeval is Y2038-unsafe

int vtimes (struct vtimes *current, struct vtimes *child)

YES, as struct_vtimes is Y2038-unsafe

Section 22.2 - Resource Limitation

rlim_t

No, as the value held is an amount of seconds, not an absolute date

int sched_rr_get_interval (pid_t pid, struct timespec *interval)

No, as the value returned is a small interval, typically 150 ┬Ás)

IOCTLs and Y2038

Some Linux IOCTLs may be Y2038-unsafe. However, at the GLIBC level, none of the IOCTLs argument types described are time_t or derived from it.

ABI changes

On 64-bits systems, time_t is already 64 bit wide Therefore, 64-bit applications should be able to directly use the (already 64-bit) time ABI, and a 64-bit GLIBC needs only provide 64-bit time support (applications might have internal bugs in their handling of time_t values, but this is not a GLIBC concern; as long as applications can exchange 64-bit time_t values with GLIBC and GLIBC processes them correctly, the existing "64-bit-time_t ABI" is safe)

On 32-bit systems, the size of time_t will be different for 32-bit and 64-bit time support. Consequently, structures containing time_t will have different sizes and field offsets. Therefore, the 64-bit-time ABI will be incompatible with the 32-bit-time ABI; and in order to remain backward-compatible with existing application binaries, a 32-bit GLIBC must keep providing the 32-bit time ABI unchanged.

Other ABI changes

(to be completed)

Implementation

In order to avoid duplicating APIs for 32-bit and 64-bit time, GLIBC will provide either one but not both for a given application; the application code will have to choose between 32-bit or 64-bit time support, and the same set of symbols (e.g. time_t or clock_gettime) will be provided in both cases.

The following is proposed:

This allows user code to check, by verifying whether __USE_TIME_BITS64 is defined once glibc headers are #included, that they are using a GLIBC which actually supports 64-bit time (or claims to).

For instance, providing 64-bit time support for the time() function would result in the following:

There is an open problem whereby incompatible kernel and GLIBC headers might be used, e.g. new, 64-bit-time, kernel headers and old, 32-bit-time, GLIBC headers. In this case, there will be a mismatch between what the GLIBC uses and what the kernel expects; for instance, the kernel will expect a 64-bit time_t while GLIBC will use a 32-bit time_t.

Support for non-Y2038-safe kernels

When running above 32-bit kernels without 64-bit time support, GLIBC will emulate a 64-bit time application API using the 32-bit kernel time API, converting 64-bit GLIBC types to and from 32-bit kernel types. Out-of-range conversions will be notified to application as appropriate using EOVERFLOW.

This will allow 64-bit-time applications to run on older kernels until (around) 2038 or until these kernels are retired, whichever happens first.

Notes

* tv_usec: to be addressed. May need GLIBC to convert structs.

* tv_nsec size: to be addressed, needs to be long as per Posix / C11.

* The implementation needs further thinking about, as application code defining _TIME_BITS=64 and gets built against

References

* Y2038 information: http://kernelnewbies.org/y2038

* Y2038 Linux patch series (WIP): https://lists.linaro.org/pipermail/y2038/2015-May/000233.html

None: Y2038ProofnessDesign (last edited 2016-06-08 13:57:53 by AlbertAribaud)