Discussion:
[PATCH v2 0/9] Y2038 struct tm related patches
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:01 UTC
Permalink
This series introduces Y2038-proof struct tm related functions.

First, it makes __tz_convert 64-bit-time compatible, as this function
is used in several struct tm related public interfaces. Then it adds
Y2038-proof versions of said interfaces.

v2:
- each commit message now mention how the patch was tested
- a ninth patch is added for mktime.c simplification and GNULIB compat.

Albert ARIBAUD (3ADEV) (7):
Y2038: make __tz_convert compatible with 64-bit-time
Y2038: add function __localtime64
Y2038: add function __localtime64_r
Y2038: add function __gmtime64
Y2038: add function __gmtime64_r
Y2038: add function __ctime64
Y2038: add function __ctime64_r

Paul Eggert (2):
Y2038: make __mktime_internal compatible with __time64_t
Fix time/mktime.c and time/gmtime.c for gnulib compatibility

include/time.h | 81 ++++++++++++++++++++++++++++++++++++++++++------
time/ctime.c | 14 +++++++++
time/ctime_r.c | 13 ++++++++
time/gmtime.c | 33 ++++++++++++++++++--
time/localtime.c | 35 ++++++++++++++++++---
time/mktime.c | 79 ++++++++++++++++++++++++++++++----------------
time/offtime.c | 12 +++----
time/timegm.c | 27 +++++++++++++++-
time/tzfile.c | 14 +++------
time/tzset.c | 27 ++++++----------
10 files changed, 258 insertions(+), 77 deletions(-)
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:03 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__localtime64): Add.
* time/localtime.c
(__localtime64): Add.
(__localtime): Compile only if __TIMERSIZE != 64.
---
include/time.h | 9 ++++++++-
time/localtime.c | 15 +++++++++++++--
2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/include/time.h b/include/time.h
index 37964f7b76..251a2b0329 100644
--- a/include/time.h
+++ b/include/time.h
@@ -56,9 +56,16 @@ extern time_t __mktime_internal (struct tm *__tp,
struct tm *(*__func) (const time_t *,
struct tm *),
long int *__offset) attribute_hidden;
+
+#if __TIMESIZE == 64
+# define __localtime64 localtime
+#else
+extern struct tm *__localtime64 (const __time64_t *__timer);
+libc_hidden_proto (__localtime64)
+#endif
+
extern struct tm *__localtime_r (const time_t *__timer,
struct tm *__tp) attribute_hidden;
-
extern struct tm *__gmtime_r (const time_t *__restrict __timer,
struct tm *__restrict __tp);
libc_hidden_proto (__gmtime_r)
diff --git a/time/localtime.c b/time/localtime.c
index 92dbfe0f8c..917e0db40a 100644
--- a/time/localtime.c
+++ b/time/localtime.c
@@ -21,7 +21,6 @@
/* The C Standard says that localtime and gmtime return the same pointer. */
struct tm _tmbuf;

-
/* Return the `struct tm' representation of *T in local time,
using *TP to store the result. */
struct tm *
@@ -31,11 +30,23 @@ __localtime_r (const time_t *t, struct tm *tp)
}
weak_alias (__localtime_r, localtime_r)

-
/* Return the `struct tm' representation of *T in local time. */
+struct tm *
+__localtime64 (const __time64_t *t)
+{
+ return __tz_convert (*t, 1, &_tmbuf);
+}
+libc_hidden_def (__localtime64)
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
struct tm *
localtime (const time_t *t)
{
return __tz_convert (*t, 1, &_tmbuf);
}
libc_hidden_def (localtime)
+
+#endif
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:02 UTC
Permalink
Now that __time64_t exists, we can switch internal function
__tz_convert from 32-bit to 64-bit time. This involves switching
some other internal functions as well, namely __tz_compute and
__offtime.

Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__tz_compute): Replace time_t with __time64_t.
(__tz_convert): Replace time_t* with __time64_t.
(__offtime): Replace time_t* with __time64_t.
* time/gmtime.c
(__gmtime_r): Adjust call to __tz_convert.
(gmtime): Likewise.
* time/localtime.c
(__localtime_r): Likewise.
(localtime): Likewise.
* time/offtime.c: Replace time_t with __time64_t.
* time/tzset.c: Likewise.
---
include/time.h | 8 ++++----
time/gmtime.c | 4 ++--
time/localtime.c | 4 ++--
time/offtime.c | 12 ++++++------
time/tzfile.c | 14 ++++----------
time/tzset.c | 27 ++++++++++-----------------
6 files changed, 28 insertions(+), 41 deletions(-)

diff --git a/include/time.h b/include/time.h
index 861b1acfb7..37964f7b76 100644
--- a/include/time.h
+++ b/include/time.h
@@ -46,7 +46,7 @@ extern void __tzfile_default (const char *std, const char *dst,
long int stdoff, long int dstoff)
attribute_hidden;
extern void __tzset_parse_tz (const char *tz) attribute_hidden;
-extern void __tz_compute (time_t timer, struct tm *tm, int use_localtime)
+extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
__THROW attribute_hidden;

/* Subroutine of `mktime'. Return the `time_t' representation of TP and
@@ -63,11 +63,11 @@ extern struct tm *__gmtime_r (const time_t *__restrict __timer,
struct tm *__restrict __tp);
libc_hidden_proto (__gmtime_r)

-/* Compute the `struct tm' representation of *T,
+/* Compute the `struct tm' representation of T,
offset OFFSET seconds east of UTC,
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
Return nonzero if successful. */
-extern int __offtime (const time_t *__timer,
+extern int __offtime (__time64_t __timer,
long int __offset,
struct tm *__tp) attribute_hidden;

@@ -76,7 +76,7 @@ extern char *__asctime_r (const struct tm *__tp, char *__buf)
extern void __tzset (void) attribute_hidden;

/* Prototype for the internal function to get information based on TZ. */
-extern struct tm *__tz_convert (const time_t *timer, int use_localtime,
+extern struct tm *__tz_convert (__time64_t timer, int use_localtime,
struct tm *tp) attribute_hidden;

extern int __nanosleep (const struct timespec *__requested_time,
diff --git a/time/gmtime.c b/time/gmtime.c
index dc33b3e68a..bda09bc021 100644
--- a/time/gmtime.c
+++ b/time/gmtime.c
@@ -23,7 +23,7 @@
struct tm *
__gmtime_r (const time_t *t, struct tm *tp)
{
- return __tz_convert (t, 0, tp);
+ return __tz_convert (*t, 0, tp);
}
libc_hidden_def (__gmtime_r)
weak_alias (__gmtime_r, gmtime_r)
@@ -33,5 +33,5 @@ weak_alias (__gmtime_r, gmtime_r)
struct tm *
gmtime (const time_t *t)
{
- return __tz_convert (t, 0, &_tmbuf);
+ return __tz_convert (*t, 0, &_tmbuf);
}
diff --git a/time/localtime.c b/time/localtime.c
index 8684a8a971..92dbfe0f8c 100644
--- a/time/localtime.c
+++ b/time/localtime.c
@@ -27,7 +27,7 @@ struct tm _tmbuf;
struct tm *
__localtime_r (const time_t *t, struct tm *tp)
{
- return __tz_convert (t, 1, tp);
+ return __tz_convert (*t, 1, tp);
}
weak_alias (__localtime_r, localtime_r)

@@ -36,6 +36,6 @@ weak_alias (__localtime_r, localtime_r)
struct tm *
localtime (const time_t *t)
{
- return __tz_convert (t, 1, &_tmbuf);
+ return __tz_convert (*t, 1, &_tmbuf);
}
libc_hidden_def (localtime)
diff --git a/time/offtime.c b/time/offtime.c
index 04c48389fc..3309fcd484 100644
--- a/time/offtime.c
+++ b/time/offtime.c
@@ -21,18 +21,18 @@
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)

-/* Compute the `struct tm' representation of *T,
+/* Compute the `struct tm' representation of T,
offset OFFSET seconds east of UTC,
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
Return nonzero if successful. */
int
-__offtime (const time_t *t, long int offset, struct tm *tp)
+__offtime (__time64_t t, long int offset, struct tm *tp)
{
- time_t days, rem, y;
+ __time64_t days, rem, y;
const unsigned short int *ip;

- days = *t / SECS_PER_DAY;
- rem = *t % SECS_PER_DAY;
+ days = t / SECS_PER_DAY;
+ rem = t % SECS_PER_DAY;
rem += offset;
while (rem < 0)
{
@@ -60,7 +60,7 @@ __offtime (const time_t *t, long int offset, struct tm *tp)
while (days < 0 || days >= (__isleap (y) ? 366 : 365))
{
/* Guess a corrected year, assuming 365 days per year. */
- time_t yg = y + days / 365 - (days % 365 < 0);
+ __time64_t yg = y + days / 365 - (days % 365 < 0);

/* Adjust DAYS and Y to match the guessed year. */
days -= ((yg - y) * 365
diff --git a/time/tzfile.c b/time/tzfile.c
index 844a68de8c..3920525bf3 100644
--- a/time/tzfile.c
+++ b/time/tzfile.c
@@ -633,16 +633,10 @@ __tzfile_compute (__time64_t timer, int use_localtime,

/* Convert to broken down structure. If this fails do not
use the string. */
- {
- time_t truncated = timer;
- if (__glibc_unlikely (truncated != timer
- || ! __offtime (&truncated, 0, tp)))
- goto use_last;
- }
-
- /* Use the rules from the TZ string to compute the change.
- timer fits into time_t due to the truncation check
- above. */
+ if (__glibc_unlikely (! __offtime (timer, 0, tp)))
+ goto use_last;
+
+ /* Use the rules from the TZ string to compute the change. */
__tz_compute (timer, tp, 1);

/* If tzspec comes from posixrules loaded by __tzfile_default,
diff --git a/time/tzset.c b/time/tzset.c
index a828b9fb75..834cc3ccec 100644
--- a/time/tzset.c
+++ b/time/tzset.c
@@ -16,7 +16,6 @@
<http://www.gnu.org/licenses/>. */

#include <ctype.h>
-#include <errno.h>
#include <libc-lock.h>
#include <stdbool.h>
#include <stddef.h>
@@ -27,7 +26,7 @@

#include <timezone/tzfile.h>

-#define SECSPERDAY ((time_t) 86400)
+#define SECSPERDAY ((__time64_t) 86400)

char *__tzname[2] = { (char *) "GMT", (char *) "GMT" };
int __daylight = 0;
@@ -55,7 +54,7 @@ typedef struct

/* We cache the computed time of change for a
given year so we don't have to recompute it. */
- time_t change; /* When to change to this zone. */
+ __time64_t change; /* When to change to this zone. */
int computed_for; /* Year above is computed for. */
} tz_rule;

@@ -416,7 +415,7 @@ tzset_internal (int always)
tz_rules[0].name = tz_rules[1].name = "UTC";
if (J0 != 0)
tz_rules[0].type = tz_rules[1].type = J0;
- tz_rules[0].change = tz_rules[1].change = (time_t) -1;
+ tz_rules[0].change = tz_rules[1].change = -1;
update_vars ();
return;
}
@@ -424,13 +423,13 @@ tzset_internal (int always)
__tzset_parse_tz (tz);
}

-/* Figure out the exact time (as a time_t) in YEAR
+/* Figure out the exact time (as a __time64_t) in YEAR
when the change described by RULE will occur and
put it in RULE->change, saving YEAR in RULE->computed_for. */
static void
compute_change (tz_rule *rule, int year)
{
- time_t t;
+ __time64_t t;

if (year != -1 && rule->computed_for == year)
/* Operations on times in 2 BC will be slower. Oh well. */
@@ -516,7 +515,7 @@ compute_change (tz_rule *rule, int year)
/* Figure out the correct timezone for TM and set `__tzname',
`__timezone', and `__daylight' accordingly. */
void
-__tz_compute (time_t timer, struct tm *tm, int use_localtime)
+__tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
{
compute_change (&tz_rules[0], 1900 + tm->tm_year);
compute_change (&tz_rules[1], 1900 + tm->tm_year);
@@ -562,20 +561,14 @@ __tzset (void)
}
weak_alias (__tzset, tzset)

-/* Return the `struct tm' representation of *TIMER in the local timezone.
+/* Return the `struct tm' representation of TIMER in the local timezone.
Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */
struct tm *
-__tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
+__tz_convert (__time64_t timer, int use_localtime, struct tm *tp)
{
long int leap_correction;
int leap_extra_secs;

- if (timer == NULL)
- {
- __set_errno (EINVAL);
- return NULL;
- }
-
__libc_lock_lock (tzset_lock);

/* Update internal database according to current TZ setting.
@@ -584,14 +577,14 @@ __tz_convert (const time_t *timer, int use_localtime, struct tm *tp)
tzset_internal (tp == &_tmbuf && use_localtime);

if (__use_tzfile)
- __tzfile_compute (*timer, use_localtime, &leap_correction,
+ __tzfile_compute (timer, use_localtime, &leap_correction,
&leap_extra_secs, tp);
else
{
if (! __offtime (timer, 0, tp))
tp = NULL;
else
- __tz_compute (*timer, tp, use_localtime);
+ __tz_compute (timer, tp, use_localtime);
leap_correction = 0L;
leap_extra_secs = 0;
}
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:04 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__localtime64_r): Add.
* time/localtime.c
(__localtime64_r): Add.
(__localtime_r): Compile only if __TIMERSIZE != 64.
---
include/time.h | 8 ++++++++
time/localtime.c | 16 ++++++++++++++++
2 files changed, 24 insertions(+)

diff --git a/include/time.h b/include/time.h
index 251a2b0329..34368295a9 100644
--- a/include/time.h
+++ b/include/time.h
@@ -66,6 +66,14 @@ libc_hidden_proto (__localtime64)

extern struct tm *__localtime_r (const time_t *__timer,
struct tm *__tp) attribute_hidden;
+
+#if __TIMESIZE == 64
+# define __localtime64_r __localtime_r
+#else
+extern struct tm *__localtime64_r (const __time64_t *__timer,
+ struct tm *__tp) attribute_hidden;
+#endif
+
extern struct tm *__gmtime_r (const time_t *__restrict __timer,
struct tm *__restrict __tp);
libc_hidden_proto (__gmtime_r)
diff --git a/time/localtime.c b/time/localtime.c
index 917e0db40a..bafea5548d 100644
--- a/time/localtime.c
+++ b/time/localtime.c
@@ -23,11 +23,27 @@ struct tm _tmbuf;

/* Return the `struct tm' representation of *T in local time,
using *TP to store the result. */
+struct tm *
+__localtime64_r (const __time64_t *t, struct tm *tp)
+{
+ return __tz_convert (*t, 1, tp);
+}
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
struct tm *
__localtime_r (const time_t *t, struct tm *tp)
{
return __tz_convert (*t, 1, tp);
}
+
+#endif
+
+/* This always works because either __TIMESIZE != 64 and __localtime_r
+ exists or __TIMESIZE == 64 and the definition of __localtime64_r above
+ actually defined __localtime_r. */
weak_alias (__localtime_r, localtime_r)

/* Return the `struct tm' representation of *T in local time. */
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:05 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__gmtime64): Add.
* time/gmtime.c
(__gmtime64): Add.
(__gmtime): Compile only if __TIMERSIZE != 64.
---
include/time.h | 4 ++++
time/gmtime.c | 14 +++++++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/include/time.h b/include/time.h
index 34368295a9..553bf74828 100644
--- a/include/time.h
+++ b/include/time.h
@@ -78,6 +78,10 @@ extern struct tm *__gmtime_r (const time_t *__restrict __timer,
struct tm *__restrict __tp);
libc_hidden_proto (__gmtime_r)

+#if __TIMESIZE == 64
+# define __gmtime64 gmtime
+#endif
+
/* Compute the `struct tm' representation of T,
offset OFFSET seconds east of UTC,
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
diff --git a/time/gmtime.c b/time/gmtime.c
index bda09bc021..46db119e9c 100644
--- a/time/gmtime.c
+++ b/time/gmtime.c
@@ -25,13 +25,25 @@ __gmtime_r (const time_t *t, struct tm *tp)
{
return __tz_convert (*t, 0, tp);
}
+
libc_hidden_def (__gmtime_r)
weak_alias (__gmtime_r, gmtime_r)

+/* Return the `struct tm' representation of *T in UTC. */
+struct tm *
+__gmtime64 (const __time64_t *t)
+{
+ return __tz_convert (*t, 0, &_tmbuf);
+}
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64

-/* Return the `struct tm' representation of *T in UTC. */
struct tm *
gmtime (const time_t *t)
{
return __tz_convert (*t, 0, &_tmbuf);
}
+
+#endif
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:07 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__ctime64): Add.
* time/gmtime.c
(__ctime64): Add.
(ctime): Compile only if __TIMERSIZE != 64.
---
include/time.h | 7 +++++++
time/ctime.c | 14 ++++++++++++++
2 files changed, 21 insertions(+)

diff --git a/include/time.h b/include/time.h
index 80543e3673..0247146211 100644
--- a/include/time.h
+++ b/include/time.h
@@ -57,6 +57,13 @@ extern time_t __mktime_internal (struct tm *__tp,
struct tm *),
long int *__offset) attribute_hidden;

+/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
+ we define __ctime64 as ctime so that nis/nis_print.c can get linked
+ against a function called ctime. */
+#if __TIMESIZE == 64
+# define __ctime64 ctime
+#endif
+
#if __TIMESIZE == 64
# define __localtime64 localtime
#else
diff --git a/time/ctime.c b/time/ctime.c
index 1222614f29..c20059c9a3 100644
--- a/time/ctime.c
+++ b/time/ctime.c
@@ -19,6 +19,18 @@

/* Return a string as returned by asctime which
is the representation of *T in that form. */
+char *
+__ctime64 (const __time64_t *t)
+{
+ /* The C Standard says ctime (t) is equivalent to asctime (localtime (t)).
+ In particular, ctime and asctime must yield the same pointer. */
+ return asctime (__localtime64 (t));
+}
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
char *
ctime (const time_t *t)
{
@@ -26,3 +38,5 @@ ctime (const time_t *t)
In particular, ctime and asctime must yield the same pointer. */
return asctime (localtime (t));
}
+
+#endif
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:08 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__ctime64_r): Add.
* time/ctime_r.c
(__ctime64_r): Add.
(__ctime_r): Compile only if __TIMERSIZE != 64.
---
include/time.h | 4 ++++
time/ctime_r.c | 13 +++++++++++++
2 files changed, 17 insertions(+)

diff --git a/include/time.h b/include/time.h
index 0247146211..c26cfb40f6 100644
--- a/include/time.h
+++ b/include/time.h
@@ -64,6 +64,10 @@ extern time_t __mktime_internal (struct tm *__tp,
# define __ctime64 ctime
#endif

+#if __TIMESIZE == 64
+# define __ctime64_r ctime_r
+#endif
+
#if __TIMESIZE == 64
# define __localtime64 localtime
#else
diff --git a/time/ctime_r.c b/time/ctime_r.c
index c111146d76..be4e62c86d 100644
--- a/time/ctime_r.c
+++ b/time/ctime_r.c
@@ -21,9 +21,22 @@

/* Return a string as returned by asctime which is the representation
of *T in that form. Reentrant version. */
+char *
+__ctime64_r (const __time64_t *t, char *buf)
+{
+ struct tm tm;
+ return __asctime_r (__localtime64_r (t, &tm), buf);
+}
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
char *
ctime_r (const time_t *t, char *buf)
{
struct tm tm;
return __asctime_r (__localtime_r (t, &tm), buf);
}
+
+#endif
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:10 UTC
Permalink
From: Paul Eggert <***@cs.ucla.edu>

Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* time/mktime.c
(__mktime64): Guard weak_alias with #ifdef
(__mktime64): Guard libc_hidden_def with #if _LIBC
(__mktime64): Guard libc_hidden_weak with #if _LIBC
(mktime): Only build when _LIBC is defined
* time/timegm.c:
(timegm): Only build when _LIBC is defined
---
time/mktime.c | 15 +++------------
time/timegm.c | 2 +-
2 files changed, 4 insertions(+), 13 deletions(-)

diff --git a/time/mktime.c b/time/mktime.c
index 6ff68f491a..b09834e47d 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -549,18 +549,14 @@ __mktime64 (struct tm *tp)
return mktime (tp);
# endif
}
-#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */

-#ifdef weak_alias
weak_alias (__mktime64, __timelocal64)
-#endif
-
-#ifdef _LIBC
libc_hidden_def (__mktime64)
libc_hidden_weak (__timelocal64)
-#endif

-#if __TIMESIZE != 64
+#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */
+
+#if defined _LIBC && __TIMESIZE != 64

/* The 32-bit-time wrapper. */
time_t
@@ -573,13 +569,8 @@ mktime (struct tm *tp)
return -1;
}

-#ifdef weak_alias
weak_alias (mktime, timelocal)
-#endif
-
-#ifdef _LIBC
libc_hidden_def (mktime)
libc_hidden_weak (timelocal)
-#endif

#endif
diff --git a/time/timegm.c b/time/timegm.c
index 027c1cf575..94dada6b88 100644
--- a/time/timegm.c
+++ b/time/timegm.c
@@ -35,7 +35,7 @@ __timegm64 (struct tm *tmp)
return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset);
}

-#if __TIMESIZE != 64
+#if defined _LIBC && __TIMESIZE != 64

time_t
timegm (struct tm *tmp)
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:09 UTC
Permalink
From: Paul Eggert <***@cs.ucla.edu>

This implies also making its callers 64-bit-time compatible
(these are mktime/localtime and timegm) and providing wrappers
for 32-bit-time userland to call.

Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h (__mktime64): Add prototype.
* include/time.h (__localtime64): Likewise.
* include/time.h (fits_in_time_t): New static function.
* time/mktime.c (__mktime64): New function.
* time/timegm.c (__timegm64): Likewise.
* time/mktime.c (mktime) [__TIMESIZE]: New wrapper function.
* time/timegm.c (timegm) [__TIMESIZE]: Likewise.
---
include/time.h | 36 +++++++++++++++++++----
time/mktime.c | 80 ++++++++++++++++++++++++++++++++++++--------------
time/timegm.c | 27 ++++++++++++++++-
3 files changed, 114 insertions(+), 29 deletions(-)

diff --git a/include/time.h b/include/time.h
index c26cfb40f6..d5e50a4e77 100644
--- a/include/time.h
+++ b/include/time.h
@@ -3,6 +3,7 @@

#ifndef _ISOMAC
# include <bits/types/locale_t.h>
+# include <stdbool.h>

extern __typeof (strftime_l) __strftime_l;
libc_hidden_proto (__strftime_l)
@@ -16,6 +17,21 @@ libc_hidden_proto (localtime)
libc_hidden_proto (strftime)
libc_hidden_proto (strptime)

+#if __TIMESIZE == 64
+# define __timegm64 timegm
+# define __mktime64 mktime
+# define __timelocal64 timelocal
+#else
+extern __time64_t __timegm64 (struct tm *__tp) __THROW;
+extern __time64_t __mktime64 (struct tm *__tp) __THROW;
+/* Another name for `__mktime64'. */
+extern __time64_t __timelocal64 (struct tm *__tp) __THROW;
+
+libc_hidden_proto (__mktime64)
+libc_hidden_proto (__timelocal64)
+#endif
+
+
extern __typeof (clock_getres) __clock_getres;
extern __typeof (clock_gettime) __clock_gettime;
libc_hidden_proto (__clock_gettime)
@@ -49,13 +65,13 @@ extern void __tzset_parse_tz (const char *tz) attribute_hidden;
extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
__THROW attribute_hidden;

-/* Subroutine of `mktime'. Return the `time_t' representation of TP and
- normalize TP, given that a `struct tm *' maps to a `time_t' as performed
+/* Subroutine of mktime. Return the __time64_t representation of TP and
+ normalize TP, given that a struct tm * maps to a __time64_t as performed
by FUNC. Record next guess for localtime-gmtime offset in *OFFSET. */
-extern time_t __mktime_internal (struct tm *__tp,
- struct tm *(*__func) (const time_t *,
- struct tm *),
- long int *__offset) attribute_hidden;
+extern __time64_t __mktime_internal (struct tm *__tp,
+ struct tm *(*__func) (const __time64_t *,
+ struct tm *),
+ long int *__offset) attribute_hidden;

/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
we define __ctime64 as ctime so that nis/nis_print.c can get linked
@@ -139,5 +155,13 @@ extern double __difftime (time_t time1, time_t time0);
actual clock ID. */
#define CLOCK_IDFIELD_SIZE 3

+/* Check whether a time64_t value fits in a time_t. */
+static inline bool
+fits_in_time_t (__time64_t t64)
+{
+ time_t t = t64;
+ return t == t64;
+}
+
#endif
#endif
diff --git a/time/mktime.c b/time/mktime.c
index 8faa9bc93d..6ff68f491a 100644
--- a/time/mktime.c
+++ b/time/mktime.c
@@ -112,11 +112,11 @@ my_tzset (void)
added to them, and then with another timestamp added, without
worrying about overflow.

- Much of the code uses long_int to represent time_t values, to
- lessen the hassle of dealing with platforms where time_t is
+ Much of the code uses long_int to represent __time64_t values, to
+ lessen the hassle of dealing with platforms where __time64_t is
unsigned, and because long_int should suffice to represent all
- time_t values that mktime can generate even on platforms where
- time_t is excessively wide. */
+ __time64_t values that mktime can generate even on platforms where
+ __time64_t is excessively wide. */

#if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60
typedef long int long_int;
@@ -144,16 +144,17 @@ shr (long_int a, int b)
: a / (one << b) - (a % (one << b) < 0));
}

-/* Bounds for the intersection of time_t and long_int. */
+/* Bounds for the intersection of __time64_t and long_int. */

static long_int const mktime_min
- = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int))
- ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t));
+ = ((TYPE_SIGNED (__time64_t)
+ && TYPE_MINIMUM (__time64_t) < TYPE_MINIMUM (long_int))
+ ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (__time64_t));
static long_int const mktime_max
- = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t)
- ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t));
+ = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (__time64_t)
+ ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (__time64_t));

-verify (TYPE_IS_INTEGER (time_t));
+verify (TYPE_IS_INTEGER (__time64_t));

#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
@@ -252,23 +253,23 @@ tm_diff (long_int year, long_int yday, int hour, int min, int sec,
}

/* Use CONVERT to convert T to a struct tm value in *TM. T must be in
- range for time_t. Return TM if successful, NULL (setting errno) on
+ range for __time64_t. Return TM if successful, NULL (setting errno) on
failure. */
static struct tm *
-convert_time (struct tm *(*convert) (const time_t *, struct tm *),
+convert_time (struct tm *(*convert) (const __time64_t *, struct tm *),
long_int t, struct tm *tm)
{
- time_t x = t;
+ __time64_t x = t;
return convert (&x, tm);
}

/* Use CONVERT to convert *T to a broken down time in *TP.
If *T is out of range for conversion, adjust it so that
it is the nearest in-range value and then convert that.
- A value is in range if it fits in both time_t and long_int.
+ A value is in range if it fits in both __time64_t and long_int.
Return TP on success, NULL (setting errno) on failure. */
static struct tm *
-ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
+ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
long_int *t, struct tm *tp)
{
long_int t1 = (*t < mktime_min ? mktime_min
@@ -310,7 +311,7 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
}


-/* Convert *TP to a time_t value, inverting
+/* Convert *TP to a __time64_t value, inverting
the monotonic and mostly-unit-linear conversion function CONVERT.
Use *OFFSET to keep track of a guess at the offset of the result,
compared to what the result would be for UTC without leap seconds.
@@ -318,9 +319,9 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
If successful, set *TP to the canonicalized struct tm;
otherwise leave *TP alone, return ((time_t) -1) and set errno.
This function is external because it is used also by timegm.c. */
-time_t
+__time64_t
__mktime_internal (struct tm *tp,
- struct tm *(*convert) (const time_t *, struct tm *),
+ struct tm *(*convert) (const __time64_t *, struct tm *),
mktime_offset_t *offset)
{
struct tm tm;
@@ -520,10 +521,13 @@ __mktime_internal (struct tm *tp,

#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS

-/* Convert *TP to a time_t value. */
-time_t
-mktime (struct tm *tp)
+/* Convert *TP to a __time64_t value. */
+__time64_t
+__mktime64 (struct tm *tp)
{
+ __time64_t t64;
+ time_t t;
+ struct tm tp0 = *tp;
/* POSIX.1 8.1.1 requires that whenever mktime() is called, the
time zone names contained in the external variable 'tzname' shall
be set as if the tzset() function had been called. */
@@ -531,7 +535,15 @@ mktime (struct tm *tp)

# if defined _LIBC || NEED_MKTIME_WORKING
static mktime_offset_t localtime_offset;
- return __mktime_internal (tp, __localtime_r, &localtime_offset);
+ t64 = __mktime_internal (&tp0, __localtime64_r, &localtime_offset);
+ t = t64;
+ if (t != t64)
+ {
+ __set_errno(EOVERFLOW);
+ return -1;
+ }
+ *tp = tp0;
+ return t;
# else
# undef mktime
return mktime (tp);
@@ -539,6 +551,28 @@ mktime (struct tm *tp)
}
#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */

+#ifdef weak_alias
+weak_alias (__mktime64, __timelocal64)
+#endif
+
+#ifdef _LIBC
+libc_hidden_def (__mktime64)
+libc_hidden_weak (__timelocal64)
+#endif
+
+#if __TIMESIZE != 64
+
+/* The 32-bit-time wrapper. */
+time_t
+mktime (struct tm *tp)
+{
+ __time64_t t64 = __mktime64 (tp);
+ if (fits_in_time_t (t64))
+ return t64;
+ __set_errno (EOVERFLOW);
+ return -1;
+}
+
#ifdef weak_alias
weak_alias (mktime, timelocal)
#endif
@@ -547,3 +581,5 @@ weak_alias (mktime, timelocal)
libc_hidden_def (mktime)
libc_hidden_weak (timelocal)
#endif
+
+#endif
diff --git a/time/timegm.c b/time/timegm.c
index 229fff23c6..027c1cf575 100644
--- a/time/timegm.c
+++ b/time/timegm.c
@@ -22,13 +22,38 @@
#endif

#include <time.h>
+#include <errno.h>

#include "mktime-internal.h"
+#include <errno.h>
+
+__time64_t
+__timegm64 (struct tm *tmp)
+{
+ static long int gmtime_offset;
+ tmp->tm_isdst = 0;
+ return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset);
+}
+
+#if __TIMESIZE != 64

time_t
timegm (struct tm *tmp)
{
+ time_t t;
+ __time64_t t64;
+ struct tm tmp0 = *tmp;
static mktime_offset_t gmtime_offset;
tmp->tm_isdst = 0;
- return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
+ t64 = __mktime_internal (&tmp0, __gmtime64_r, &gmtime_offset);
+ t = t64;
+ if (t != t64)
+ {
+ __set_errno(EOVERFLOW);
+ return -1;
+ }
+ *tmp = tmp0;
+ return t;
}
+
+#endif
--
2.17.1
Albert ARIBAUD (3ADEV)
2018-11-29 22:10:06 UTC
Permalink
Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu.

* include/time.h
(__gmtime64_r): Add.
* time/gmtime.c
(__gmtime64_r): Add.
(__gmtime): Compile only if __TIMERSIZE != 64.
---
include/time.h | 7 +++++++
time/gmtime.c | 15 +++++++++++++++
2 files changed, 22 insertions(+)

diff --git a/include/time.h b/include/time.h
index 553bf74828..80543e3673 100644
--- a/include/time.h
+++ b/include/time.h
@@ -82,6 +82,13 @@ libc_hidden_proto (__gmtime_r)
# define __gmtime64 gmtime
#endif

+#if __TIMESIZE == 64
+# define __gmtime64_r __gmtime_r
+#else
+extern struct tm *__gmtime64_r (const __time64_t *__restrict __timer,
+ struct tm *__restrict __tp);
+#endif
+
/* Compute the `struct tm' representation of T,
offset OFFSET seconds east of UTC,
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
diff --git a/time/gmtime.c b/time/gmtime.c
index 46db119e9c..4674ffe01e 100644
--- a/time/gmtime.c
+++ b/time/gmtime.c
@@ -20,12 +20,27 @@

/* Return the `struct tm' representation of *T in UTC,
using *TP to store the result. */
+struct tm *
+__gmtime64_r (const __time64_t *t, struct tm *tp)
+{
+ return __tz_convert (*t, 0, tp);
+}
+
+/* Provide a 32-bit variant if needed */
+
+#if __TIMESIZE != 64
+
struct tm *
__gmtime_r (const time_t *t, struct tm *tp)
{
return __tz_convert (*t, 0, tp);
}

+#endif
+
+/* This always works because either __TIMESIZE != 64 and __gmtime_r exists
+ or __TIMESIZE == 64 and the definition of __gmtime64_r above actually
+ defined __gmtime_r. */
libc_hidden_def (__gmtime_r)
weak_alias (__gmtime_r, gmtime_r)
--
2.17.1
Albert ARIBAUD
2018-12-05 00:11:40 UTC
Permalink
On Thu, 29 Nov 2018 23:10:01 +0100, "Albert ARIBAUD (3ADEV)"
Post by Albert ARIBAUD (3ADEV)
This series introduces Y2038-proof struct tm related functions.
First, it makes __tz_convert 64-bit-time compatible, as this function
is used in several struct tm related public interfaces. Then it adds
Y2038-proof versions of said interfaces.
- each commit message now mention how the patch was tested
- a ninth patch is added for mktime.c simplification and GNULIB compat.
Any comments on these?

Cordialement,
Albert ARIBAUD
3ADEV
Joseph Myers
2018-12-05 16:49:26 UTC
Permalink
Patch 1 is OK.

For most of the rest of the series, I think there is a general issue to
resolve regarding how we document (internally) the symbol handling for
Post by Albert ARIBAUD (3ADEV)
+/* This always works because either __TIMESIZE != 64 and __gmtime_r exists
+ or __TIMESIZE == 64 and the definition of __gmtime64_r above actually
+ defined __gmtime_r. */
But this is nothing to do with those particular symbols; it's completely
generic, as part of the way time-related symbols are handled in this
series, that each such function for time_t is either defined explicitly,
or via a #define of the name used with __time64_t. So it doesn't deserve
a comment there, any more than each use of libc_hidden_def or weak_alias
deserves a comment on the generic symbol handling conventions in glibc
that result in those uses (only those with something unusual about them
should have such comments).

Now, those conventions probably *do* deserve to be documented somewhere
(in the glibc sources, not just on the wiki), but somewhere not tied to
one particular function. I think a better place might be maint.texi: add
a section "Symbol Handling" or similar, which initially would just discuss
how symbols for 64-bit time are handled (both on systems that had it all
along, and on systems where support is being added alongside existing
support for 32-bit time), but could be expanded in future to discuss the
various other symbol handling issues in glibc.
Post by Albert ARIBAUD (3ADEV)
+/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
+ we define __ctime64 as ctime so that nis/nis_print.c can get linked
+ against a function called ctime. */
(a) __ctime64 is defined as ctime as part of the general symbol
conventions for such symbols, since ctime is unconditionally part of the
glibc ABI and API. It's nothing to do with a particular use in
nis/nis_print.c. (Once that comment is accordingly removed, no further
action is needed for (b) and (c) below; they are just general observations
on related issues.)

(b) Any such non-obvious reference in a comment from one part of the glibc
source to another really needs a corresponding comment pointing in the
other direction, so that someone changing nis/nis_print.c would know they
might need to update the other comment.

(c) In general, code internal to glibc should change to use 64-bit time
explicitly rather than the old time_t / ctime. In this particular case,
(i) it probably can't do so yet because you can't changes users outside of
libc.so until libc.so exports the new interfaces, and (ii) the change
might not provide much benefit, if the 32-bit times are part of the NIS
protocol (which would mean NIS is obsolete by Y2038) - although it may be
a good idea anyway to get rid of glibc-internal uses of the 32-bit
interfaces.
--
Joseph S. Myers
***@codesourcery.com
Albert ARIBAUD
2018-12-05 18:40:59 UTC
Permalink
Hi Joseph,

On Wed, 5 Dec 2018 16:49:26 +0000, Joseph Myers
Post by Joseph Myers
Patch 1 is OK.
Thanks! Accordingly, I'll apply this patch on master and re-spin the
series above it.
Post by Joseph Myers
For most of the rest of the series, I think there is a general issue to
resolve regarding how we document (internally) the symbol handling for
Post by Albert ARIBAUD (3ADEV)
+/* This always works because either __TIMESIZE != 64 and __gmtime_r exists
+ or __TIMESIZE == 64 and the definition of __gmtime64_r above actually
+ defined __gmtime_r. */
But this is nothing to do with those particular symbols; it's completely
generic, as part of the way time-related symbols are handled in this
series, that each such function for time_t is either defined explicitly,
or via a #define of the name used with __time64_t. So it doesn't deserve
a comment there, any more than each use of libc_hidden_def or weak_alias
deserves a comment on the generic symbol handling conventions in glibc
that result in those uses (only those with something unusual about them
should have such comments).
Now, those conventions probably *do* deserve to be documented somewhere
(in the glibc sources, not just on the wiki), but somewhere not tied to
one particular function. I think a better place might be maint.texi: add
a section "Symbol Handling" or similar, which initially would just discuss
how symbols for 64-bit time are handled (both on systems that had it all
along, and on systems where support is being added alongside existing
support for 32-bit time), but could be expanded in future to discuss the
various other symbol handling issues in glibc.
I will prepend a patch to the (re-spun) series to add a section
"Symbol handling" to maint.texi, with a subsection "Y2038 symbol
handling" in which I will document these conventions.
Post by Joseph Myers
Post by Albert ARIBAUD (3ADEV)
+/* nis/nis_print.c needs ctime, so even if ctime is not declared here,
+ we define __ctime64 as ctime so that nis/nis_print.c can get linked
+ against a function called ctime. */
(a) __ctime64 is defined as ctime as part of the general symbol
conventions for such symbols, since ctime is unconditionally part of the
glibc ABI and API. It's nothing to do with a particular use in
nis/nis_print.c. (Once that comment is accordingly removed, no further
action is needed for (b) and (c) below; they are just general observations
on related issues.)
(b) Any such non-obvious reference in a comment from one part of the glibc
source to another really needs a corresponding comment pointing in the
other direction, so that someone changing nis/nis_print.c would know they
might need to update the other comment.
(c) In general, code internal to glibc should change to use 64-bit time
explicitly rather than the old time_t / ctime. In this particular case,
(i) it probably can't do so yet because you can't changes users outside of
libc.so until libc.so exports the new interfaces, and (ii) the change
might not provide much benefit, if the 32-bit times are part of the NIS
protocol (which would mean NIS is obsolete by Y2038) - although it may be
a good idea anyway to get rid of glibc-internal uses of the 32-bit
interfaces.
Understood.

Cordialement,
Albert ARIBAUD
3ADEV

Loading...