[PATCH 0/6] y2038: Linux: Provide __clock_* functions supporting 64 bit time

classic Classic list List threaded Threaded
92 messages Options
12345
Reply | Threaded
Open this post in threaded view
|

[PATCH 0/6] y2038: Linux: Provide __clock_* functions supporting 64 bit time

Lukasz Majewski
This patch set introduces the conversion of __clock_{settime|
gettime|getres|nanosleep} to explicit 64 bit struct __timespec64
arguments. As a result those functions are now Y2038 safe

To achieve the above goal - new, internal to glibc,
struct __timespec64 has been also introduced as well as
helper functions to handle time conversions on 32 bit systems.

The code from this patch set uses new clock_* syscalls (explicit
64 bits) from Linux kernel (5.1+) as well

This work is (loosely) based on a previous development/patches:
https://libc-alpha.sourceware.narkive.com/zniMOWui/rfc-patch-00-52-make-glibc-y2038-proof#post68

Those patches shall be applied on top of:
https://github.com/lmajewski/y2038_glibc/commit/c17d6107d205026f476076948269763f5d177481
https://github.com/lmajewski/y2038_glibc/commit/a9835a888842078ea4d317086375d8f518a490d7

Github branch (including the y2038 conversion example):
https://github.com/lmajewski/y2038_glibc/commits/Y2038-2.29-glibc-__clock-internal-struct-timespec-v1


Shall be used with provided meta-y2038 for development and testing:
https://github.com/lmajewski/meta-y2038

I've used guidelines from:
https://www.gnu.org/software/libc/manual/html_mono/libc.html
"D.2.1 64-bit time symbol handling in the GNU C Library"
to convert *clock_settime*.

and most notably from:
https://sourceware.org/glibc/wiki/Y2038ProofnessDesign#clock_gettime.28.29

Comments are more than welcome.


Lukasz Majewski (6):
  y2038: Introduce internal for glibc struct __timespec64
  y2038: Provide conversion helpers for struct __timespec64
  y2038: linux: Provide __clock_settime64 implementation
  y2038: linux: Provide __clock_getres64 implementation
  y2038: linux: Provide __clock_gettime64 implementation
  y2038: linux: Provide __clock_nanosleep64 implementation

 include/time.h                            | 142 ++++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/clock_getres.c    |  33 ++++++-
 sysdeps/unix/sysv/linux/clock_gettime.c   |  33 ++++++-
 sysdeps/unix/sysv/linux/clock_nanosleep.c |  52 ++++++++++-
 sysdeps/unix/sysv/linux/clock_settime.c   |  34 ++++++-
 5 files changed, 285 insertions(+), 9 deletions(-)

--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 1/6] y2038: Introduce internal for glibc struct __timespec64

Lukasz Majewski
This type is a glibc's type similar to struct timespec
but whose tv_sec field is a __time64_t rather than a time_t,
which makes it Y2038-proof and usable to pass between user
code and Y2038-proof kernel syscalls (e.g. clock_gettime()).

To support passing this structure to the kernel - the tv_pad,
32 bit padding field has been introduced. The placement of it
depends on endiannes of the SoC.

Tested on x86_64 and ARM.

* include/time.h: Add struct __timespec64 definition
---
 include/time.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/include/time.h b/include/time.h
index ac3163c2a5..a4a06201d5 100644
--- a/include/time.h
+++ b/include/time.h
@@ -5,6 +5,7 @@
 # include <bits/types/locale_t.h>
 # include <stdbool.h>
 # include <time/mktime-internal.h>
+# include <endian.h>
 
 extern __typeof (strftime_l) __strftime_l;
 libc_hidden_proto (__strftime_l)
@@ -52,6 +53,27 @@ extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime)
   __THROW attribute_hidden;
 
 #if __TIMESIZE == 64
+# define __timespec64 timespec
+#else
+/* The glibc Y2038-proof struct __timespec64 structure for a time value.
+   To keep things Posix-ish, we keep the nanoseconds field a 32-bit
+   signed long, but since the Linux field is a 64-bit signed int, we
+   pad our tv_nsec with a 32-bit bitfield, which should always be 0. */
+
+struct __timespec64
+{
+  __time64_t tv_sec;         /* Seconds */
+# if BYTE_ORDER == BIG_ENDIAN
+  int tv_pad: 32;            /* Padding named for checking/setting */
+  __int32_t tv_nsec;         /* Nanoseconds */
+# else
+  __int32_t tv_nsec;         /* Nanoseconds */
+  int tv_pad: 32;            /* Padding named for checking/setting */
+# endif
+};
+#endif
+
+#if __TIMESIZE == 64
 # define __ctime64 ctime
 #else
 extern char *__ctime64 (const __time64_t *__timer) __THROW;
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 2/6] y2038: Provide conversion helpers for struct __timespec64

Lukasz Majewski
In reply to this post by Lukasz Majewski
Those functions allow easy conversion between Y2038 safe struct
 __timespec64 and other time related data structures.

Moreover, those functions are NOT compiled when one runs 64 bit system
(the __TIMESIZE == 64) and are used only for 32 bit wrappers (
like __clock_gettime).

* include/time.h (valid_timeval_to_timespec64): Add.
* include/time.h (valid_timespec_to_timespec64): Likewise.
* include/time.h (valid_timespec64_to_timespec): Likewise.
* include/time.h (valid_timespec64_to_timeval): Likewise.
* include/time.h (IS_VALID_NANOSECONDS): Likewise.
* include/time.h (timespec_to_timespec64): Likewise.
* include/time.h (timespec64_to_timespec): Likewise.
* include/time.h (timespec64_to_timeval): Likewise.
---
 include/time.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/include/time.h b/include/time.h
index a4a06201d5..9eed500cf1 100644
--- a/include/time.h
+++ b/include/time.h
@@ -178,5 +178,92 @@ in_time_t_range (__time64_t t)
   return s == t;
 }
 
+# if __TIMESIZE != 64
+/* Convert a known valid struct timeval into a struct __timespec64.  */
+static inline void
+valid_timeval_to_timespec64 (const struct timeval *tv32,
+     struct __timespec64 *ts64)
+{
+  ts64->tv_sec = tv32->tv_sec;
+  ts64->tv_nsec = tv32->tv_usec * 1000;
+}
+
+/* Convert a known valid struct timespec into a struct __timespec64.  */
+static inline void
+valid_timespec_to_timespec64 (const struct timespec *ts32,
+      struct __timespec64 *ts64)
+{
+  ts64->tv_sec = ts32->tv_sec;
+  ts64->tv_nsec = ts32->tv_nsec;
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+}
+
+/* Convert a known valid struct __timespec64 into a struct timespec.  */
+static inline void
+valid_timespec64_to_timespec (const struct __timespec64 *ts64,
+      struct timespec *ts32)
+{
+  ts32->tv_sec = (time_t) ts64->tv_sec;
+  ts32->tv_nsec = ts64->tv_nsec;
+}
+
+/* Convert a known valid struct __timespec64 into a struct timeval.  */
+static inline void
+valid_timespec64_to_timeval (const struct __timespec64 *ts64,
+     struct timeval *tv32)
+{
+  tv32->tv_sec = (time_t) ts64->tv_sec;
+  tv32->tv_usec = ts64->tv_nsec / 1000;
+}
+
+/* Check if a value lies with the valid nanoseconds range.  */
+#define IS_VALID_NANOSECONDS(ns) ((ns) >= 0 && (ns) <= 999999999)
+
+/* Check and convert a struct timespec into a struct __timespec64.  */
+static inline bool
+timespec_to_timespec64 (const struct timespec *ts32,
+                        struct __timespec64 *ts64)
+{
+  /* Check that ts32 holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts32->tv_nsec))
+    return false;
+  /* All ts32 fields can fit in ts64, so copy them.  */
+  valid_timespec_to_timespec64 (ts32, ts64);
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timespec.  */
+static inline bool
+timespec64_to_timespec (const struct __timespec64 *ts64,
+                        struct timespec *ts32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! in_time_t_range (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in ts32, so copy them.  */
+  valid_timespec64_to_timespec (ts64, ts32);
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timeval.  */
+static inline bool
+timespec64_to_timeval (const struct __timespec64 *ts64,
+                       struct timeval *tv32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! in_time_t_range (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in tv32, so copy them.  */
+  valid_timespec64_to_timeval (ts64, tv32);
+  return true;
+}
+# endif
 #endif
 #endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Lukasz Majewski
In reply to this post by Lukasz Majewski
This patch provides new __clock_settime64 explicit 64 bit function for
setting the time. Moreover, a 32 bit version - __clock_settime has been
refactored to internally use __clock_settime64.

The __clock_settime is now supposed to be used on 32 bit systems -
hence the necessary checks and conversion to 64 bit type. After this
change it is intrinsically Y2038 safe.

The new 64 bit syscall (clock_settime64) available from Linux
5.1+ has been used when applicable on 32 bit systems.
The execution path on 64 bit systems has not been changed or affected in
any way.

Tests:
- The code has been tested with x86_64/x86 (native compilation):
make PARALLELMFLAGS="-j8" && make xcheck PARALLELMFLAGS="-j8"

- Run specific tests on ARM/x86 32bit systems (qemu):
https://github.com/lmajewski/meta-y2038
and run tests:
https://github.com/lmajewski/y2038-tests/commits/master
on kernels with and without 64 bit time support.

No regressions were observed.

* include/time.h (__clock_settime64):
  Add __clock_settime alias according to __TIMESIZE define
* sysdeps/unix/sysv/linux/clock_settime.c (__clock_settime):
  Refactor this function to be used only on 32 bit machines as a wrapper
  on __clock_settime64.
* sysdeps/unix/sysv/linux/clock_settime.c (__clock_settime64): Add
* sysdeps/unix/sysv/linux/clock_settime.c (__clock_settime64):
  Use clock_settime64 kernel syscall (available from 5.1-rc1+ Linux) by
  32 bit Y2038 safe systems
---
 include/time.h                          |  8 ++++++++
 sysdeps/unix/sysv/linux/clock_settime.c | 34 ++++++++++++++++++++++++++++++---
 2 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/include/time.h b/include/time.h
index 9eed500cf1..33eee8eb9a 100644
--- a/include/time.h
+++ b/include/time.h
@@ -124,6 +124,14 @@ extern __time64_t __timegm64 (struct tm *__tp) __THROW;
 libc_hidden_proto (__timegm64)
 #endif
 
+#if __TIMESIZE == 64
+# define __clock_settime64 __clock_settime
+#else
+extern int __clock_settime64 (clockid_t clock_id,
+                              const struct __timespec64 *tp);
+libc_hidden_proto (__clock_settime64)
+#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/sysdeps/unix/sysv/linux/clock_settime.c b/sysdeps/unix/sysv/linux/clock_settime.c
index d837e3019c..a1ea1cac28 100644
--- a/sysdeps/unix/sysv/linux/clock_settime.c
+++ b/sysdeps/unix/sysv/linux/clock_settime.c
@@ -19,11 +19,9 @@
 #include <sysdep.h>
 #include <time.h>
 
-#include "kernel-posix-cpu-timers.h"
-
 /* Set CLOCK to value TP.  */
 int
-__clock_settime (clockid_t clock_id, const struct timespec *tp)
+__clock_settime64 (clockid_t clock_id, const struct __timespec64 *tp)
 {
   /* Make sure the time cvalue is OK.  */
   if (tp->tv_nsec < 0 || tp->tv_nsec >= 1000000000)
@@ -32,6 +30,36 @@ __clock_settime (clockid_t clock_id, const struct timespec *tp)
       return -1;
     }
 
+#if defined (__TIMESIZE) && __TIMESIZE != 64
+# if defined __NR_clock_settime64
+  /* Make sure that passed __timespec64 struct pad is 0.  */
+  struct __timespec64 ts = *tp;
+  ts.tv_pad = 0;
+  return INLINE_SYSCALL_CALL (clock_settime64, clock_id, &ts);
+# else
+  struct timespec ts32;
+  valid_timespec64_to_timespec(tp, &ts32);
+  return INLINE_SYSCALL_CALL (clock_settime, clock_id, &ts32);
+# endif
+#else
   return INLINE_SYSCALL_CALL (clock_settime, clock_id, tp);
+#endif
 }
 weak_alias (__clock_settime, clock_settime)
+
+#if __TIMESIZE != 64
+int
+__clock_settime (clockid_t clock_id, const struct timespec *tp)
+{
+  struct __timespec64 ts64;
+
+  if (! in_time_t_range (tp->tv_sec))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  valid_timespec_to_timespec64 (tp, &ts64);
+  return __clock_settime64 (clock_id, &ts64);
+}
+#endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 4/6] y2038: linux: Provide __clock_getres64 implementation

Lukasz Majewski
In reply to this post by Lukasz Majewski
This patch provides new __clock_getres64 explicit 64 bit function for
getting the resolution (precision) of specified clock ID. Moreover, a
32 bit version - __clock_getres has been refactored to internally use
__clock_getres64.

The __clock_getres is now supposed to be used on 32 bit systems -
hence the necessary checks and conversion to 64 bit type. After this
change it is intrinsically Y2038 safe.

The new 64 bit syscall (clock_getres_time64) available from Linux
5.1+ has been used when applicable on 32 bit systems.
The execution path on 64 bit systems has not been changed or affected
in any way.

Tests:
- The code has been tested with x86_64/x86 (native compilation):
make PARALLELMFLAGS="-j8" && make xcheck PARALLELMFLAGS="-j8"

- Run specific tests on ARM/x86 32bit systems (qemu):
https://github.com/lmajewski/meta-y2038
and run tests:
https://github.com/lmajewski/y2038-tests/commits/master
on kernels with and without 64 bit time support.

No regressions were observed.

* include/time.h (__clock_getres64):
  Add __clock_getres alias according to __TIMESIZE define
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres):
  Refactor this function to be used only on 32 bit machines as a
  wrapper on __clock_getres64.
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres64): Add
* sysdeps/unix/sysv/linux/clock_getres.c (__clock_getres64):
  Use clock_getres_time6464 kernel syscall (available from 5.1-rc1+
  Linux) by 32 bit Y2038 safe systems
---
 include/time.h                         |  8 ++++++++
 sysdeps/unix/sysv/linux/clock_getres.c | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/include/time.h b/include/time.h
index 33eee8eb9a..20b9974078 100644
--- a/include/time.h
+++ b/include/time.h
@@ -132,6 +132,14 @@ extern int __clock_settime64 (clockid_t clock_id,
 libc_hidden_proto (__clock_settime64)
 #endif
 
+#if __TIMESIZE == 64
+# define __clock_getres64 __clock_getres
+#else
+extern int __clock_getres64 (clockid_t clock_id,
+                             struct __timespec64 *tp);
+libc_hidden_proto (__clock_getres64);
+#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/sysdeps/unix/sysv/linux/clock_getres.c b/sysdeps/unix/sysv/linux/clock_getres.c
index 24b2299938..3f6c24f1dd 100644
--- a/sysdeps/unix/sysv/linux/clock_getres.c
+++ b/sysdeps/unix/sysv/linux/clock_getres.c
@@ -28,8 +28,39 @@
 
 /* Get resolution of clock.  */
 int
-__clock_getres (clockid_t clock_id, struct timespec *res)
+__clock_getres64 (clockid_t clock_id, struct __timespec64 *res)
 {
+#if defined (__TIMESIZE) && __TIMESIZE != 64
+# if defined __NR_clock_getres_time64
+  return INLINE_VSYSCALL (clock_getres_time64, 2, clock_id, res);
+# else
+  struct timespec ts32;
+  int retval = INLINE_VSYSCALL (clock_getres, 2, clock_id, &ts32);
+  if (! retval)
+    valid_timespec_to_timespec64(&ts32, res);
+
+  return retval;
+# endif
+#else
   return INLINE_VSYSCALL (clock_getres, 2, clock_id, res);
+#endif
 }
 weak_alias (__clock_getres, clock_getres)
+
+#if __TIMESIZE != 64
+int
+__clock_getres (clockid_t clock_id, struct timespec *res)
+{
+  struct __timespec64 ts64;
+  int retval;
+
+  retval = __clock_getres64 (clock_id, &ts64);
+  if (! retval && res && ! timespec64_to_timespec (&ts64, res))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  return retval;
+}
+#endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 5/6] y2038: linux: Provide __clock_gettime64 implementation

Lukasz Majewski
In reply to this post by Lukasz Majewski
This patch provides new __clock_gettime64 explicit 64 bit function for
getting the value of specified clock ID. Moreover, a
32 bit version - __clock_gettime has been refactored to internally use
__clock_gettime64.

The __clock_gettime is now supposed to be used on 32 bit systems -
hence the necessary checks and conversion to 64 bit type. After this
change it is intrinsically Y2038 safe.

The new 64 bit syscall (clock_gettime64) available from Linux
5.1+ has been used when applicable on 32 bit systems.
The execution path on 64 bit systems has not been changed or affected
in any way.

Tests:
- The code has been tested with x86_64/x86 (native compilation):
make PARALLELMFLAGS="-j8" && make xcheck PARALLELMFLAGS="-j8"

- Run specific tests on ARM/x86 32bit systems (qemu):
https://github.com/lmajewski/meta-y2038
and run tests:
https://github.com/lmajewski/y2038-tests/commits/master
on kernels with and without 64 bit time support.

No regressions were observed.

* include/time.h (__clock_gettime64):
  Add __clock_gettime alias according to __TIMESIZE define
* sysdeps/unix/sysv/linux/clock_gettime.c (__clock_gettime):
  Refactor this function to be used only on 32 bit machines as a
  wrapper on __clock_gettime64.
* sysdeps/unix/sysv/linux/clock_gettime.c (__clock_gettime64): Add
* sysdeps/unix/sysv/linux/clock_gettime.c (__clock_gettime64):
  Use clock_gettime64 kernel syscall (available from 5.1-rc1+
  Linux) by 32 bit Y2038 safe systems
---
 include/time.h                          |  8 ++++++++
 sysdeps/unix/sysv/linux/clock_gettime.c | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/include/time.h b/include/time.h
index 20b9974078..7176ddbb84 100644
--- a/include/time.h
+++ b/include/time.h
@@ -133,6 +133,14 @@ libc_hidden_proto (__clock_settime64)
 #endif
 
 #if __TIMESIZE == 64
+# define __clock_gettime64 __clock_gettime
+#else
+extern int __clock_gettime64 (clockid_t clock_id,
+                              struct __timespec64 *tp);
+libc_hidden_proto (__clock_gettime64);
+#endif
+
+#if __TIMESIZE == 64
 # define __clock_getres64 __clock_getres
 #else
 extern int __clock_getres64 (clockid_t clock_id,
diff --git a/sysdeps/unix/sysv/linux/clock_gettime.c b/sysdeps/unix/sysv/linux/clock_gettime.c
index 5fc47fb7dc..fd94c6fec4 100644
--- a/sysdeps/unix/sysv/linux/clock_gettime.c
+++ b/sysdeps/unix/sysv/linux/clock_gettime.c
@@ -28,9 +28,40 @@
 
 /* Get current value of CLOCK and store it in TP.  */
 int
-__clock_gettime (clockid_t clock_id, struct timespec *tp)
+__clock_gettime64 (clockid_t clock_id, struct __timespec64 *tp)
 {
+#if defined (__TIMESIZE) && __TIMESIZE != 64
+# if defined __NR_clock_gettime64
+  return INLINE_SYSCALL_CALL (clock_gettime64, clock_id, tp);
+# else
+  struct timespec ts32;
+  int retval = INLINE_VSYSCALL (clock_gettime, 2, clock_id, &ts32);
+  if (! retval)
+    valid_timespec_to_timespec64(&ts32, tp);
+
+  return retval;
+# endif
+#else
   return INLINE_VSYSCALL (clock_gettime, 2, clock_id, tp);
+#endif
 }
 weak_alias (__clock_gettime, clock_gettime)
 libc_hidden_def (__clock_gettime)
+
+#if __TIMESIZE != 64
+int
+__clock_gettime (clockid_t clock_id, struct timespec *tp)
+{
+  struct __timespec64 ts64;
+  int retval;
+
+  retval = __clock_gettime64 (clock_id, &ts64);
+  if (! retval && ! timespec64_to_timespec (&ts64, tp))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  return retval;
+}
+#endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH 6/6] y2038: linux: Provide __clock_nanosleep64 implementation

Lukasz Majewski
In reply to this post by Lukasz Majewski
This patch provides new __clock_nanosleep64 explicit 64 bit function
for sleeping specified time on specified clock ID. Moreover, a
32 bit version - __clock_nanosleep has been refactored to internally
use __clock_nanosleep64.

The __clock_nanosleep is now supposed to be used on 32 bit systems -
hence the necessary checks and conversions to 64 bit type. After this
change it is intrinsically Y2038 safe.

The new 64 bit syscall (clock_nanosleep_time64) available from Linux
5.1+ has been used when applicable on 32 bit systems.
The execution path on 64 bit systems has not been changed or affected
in any way.

Tests:
- The code has been tested with x86_64/x86 (native compilation):
make PARALLELMFLAGS="-j8" && make xcheck PARALLELMFLAGS="-j8"

- Run specific tests on ARM/x86 32bit systems (qemu):
https://github.com/lmajewski/meta-y2038
and run tests:
https://github.com/lmajewski/y2038-tests/commits/master
on kernels with and without 64 bit time support.

No regressions were observed.

* include/time.h (__clock_nanosleep64):
  Add __clock_nanosleep alias according to __TIMESIZE define
* sysdeps/unix/sysv/linux/clock_nanosleep.c (__clock_nanosleep):
  Refactor this function to be used only on 32 bit machines as a
  wrapper on __clock_nanosleep64.
* sysdeps/unix/sysv/linux/clock_nanosleep.c (__clock_nanosleep64):
  Add
* sysdeps/unix/sysv/linux/clock_nanosleep.c (__clock_nanosleep64):
  Use clock_nanosleep_time64 kernel syscall (available from
  5.1-rc1+ Linux) by 32 bit Y2038 safe systems
---
 include/time.h                            |  9 ++++++
 sysdeps/unix/sysv/linux/clock_nanosleep.c | 52 ++++++++++++++++++++++++++++---
 2 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/include/time.h b/include/time.h
index 7176ddbb84..75f3456017 100644
--- a/include/time.h
+++ b/include/time.h
@@ -148,6 +148,15 @@ extern int __clock_getres64 (clockid_t clock_id,
 libc_hidden_proto (__clock_getres64);
 #endif
 
+#if __TIMESIZE == 64
+# define __clock_nanosleep64 __clock_nanosleep
+#else
+extern int __clock_nanosleep64 (clockid_t clock_id, int flags,
+                                const struct __timespec64 *req,
+                                struct __timespec64 *rem);
+libc_hidden_proto (__clock_nanosleep64);
+#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/sysdeps/unix/sysv/linux/clock_nanosleep.c b/sysdeps/unix/sysv/linux/clock_nanosleep.c
index 0cb6614dc9..3f99b5df00 100644
--- a/sysdeps/unix/sysv/linux/clock_nanosleep.c
+++ b/sysdeps/unix/sysv/linux/clock_nanosleep.c
@@ -25,8 +25,8 @@
 /* We can simply use the syscall.  The CPU clocks are not supported
    with this function.  */
 int
-__clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
-   struct timespec *rem)
+__clock_nanosleep64 (clockid_t clock_id, int flags,
+                     const struct __timespec64 *req, struct __timespec64 *rem)
 {
   if (clock_id == CLOCK_THREAD_CPUTIME_ID)
     return EINVAL;
@@ -36,9 +36,53 @@ __clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
   /* If the call is interrupted by a signal handler or encounters an error,
      it returns a positive value similar to errno.  */
   INTERNAL_SYSCALL_DECL (err);
+#if defined (__TIMESIZE) && __TIMESIZE != 64
+# if defined __NR_clock_nanosleep_time64
+  struct __timespec64 ts = *req;
+  ts.tv_pad = 0;
+  int r = INTERNAL_SYSCALL_CANCEL (clock_nanosleep_time64, err, clock_id,
+                                   flags, &ts, rem);
+# else
+  struct timespec req32, rem32;
+  valid_timespec64_to_timespec(req, &req32);
+  int r = INTERNAL_SYSCALL_CANCEL (clock_nanosleep, err, clock_id, flags,
+                                   &req32, &rem32);
+
+  if (! INTERNAL_SYSCALL_ERROR_P (r, err))
+      valid_timespec_to_timespec64(&rem32, rem);
+# endif
+#else
   int r = INTERNAL_SYSCALL_CANCEL (clock_nanosleep, err, clock_id, flags,
-   req, rem);
+                                   req, rem);
+#endif
   return (INTERNAL_SYSCALL_ERROR_P (r, err)
-  ? INTERNAL_SYSCALL_ERRNO (r, err) : 0);
+          ? INTERNAL_SYSCALL_ERRNO (r, err) : 0);
 }
 weak_alias (__clock_nanosleep, clock_nanosleep)
+
+#if __TIMESIZE != 64
+int
+__clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *req,
+                   struct timespec *rem)
+{
+  struct __timespec64 req64, rem64;
+  int retval;
+
+  if (! in_time_t_range (req->tv_sec))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  valid_timespec_to_timespec64 (req, &req64);
+  retval = __clock_nanosleep64 (clock_id, flags, &req64, &rem64);
+
+  if (! retval && rem && ! timespec64_to_timespec (&rem64, rem))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  return retval;
+}
+#endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Joseph Myers
In reply to this post by Lukasz Majewski
On Mon, 15 Apr 2019, Lukasz Majewski wrote:

> The new 64 bit syscall (clock_settime64) available from Linux
> 5.1+ has been used when applicable on 32 bit systems.
> The execution path on 64 bit systems has not been changed or affected in
> any way.

Is this unchanged specifically because of __TIMESIZE conditionals in the
code (so that it doesn't matter whether the 64-bit systems define any new
syscall names)?

Also, I don't see any __ASSUME_* / ENOSYS handling in this patch.  As
usual, for any new syscall that might be used in code that could also use
an older syscall:

* If the appropriate __ASSUME_* is defined, the new syscall can simply be
used unconditionally.

* If the appropriate __ASSUME_* is not defined, there needs to be runtime
fallback to the old syscall if the new one returns an ENOSYS error (of
course, that runtime fallback needs to check for the time overflowing the
32-bit range and give an appropriate error in that case, before calling
the 32-bit syscall - and it's necessary to figure out what the priority
should be of errors for invalid nanoseconds values versus overflowing
seconds).  It is normal and expected for the kernel headers used to build
glibc to be (much) newer than the oldest kernel version supported by the
resulting glibc binaries at runtime.

This __ASSUME_* macro definition in kernel-features.h would need a
multi-paragraph comment discussing exactly what the semantics of such
__ASSUME_* macros are in the context of the sets of syscall names and
numbers present on different kinds of Linux kernel architectures.

> +#if __TIMESIZE != 64
> +int
> +__clock_settime (clockid_t clock_id, const struct timespec *tp)
> +{
> +  struct __timespec64 ts64;
> +
> +  if (! in_time_t_range (tp->tv_sec))
> +    {
> +      __set_errno (EOVERFLOW);
> +      return -1;
> +    }

I don't see how an overflow check makes sense in this context (converting
a 32-bit timespec to a 64-bit one).

--
Joseph S. Myers
[hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 6/6] y2038: linux: Provide __clock_nanosleep64 implementation

Joseph Myers
In reply to this post by Lukasz Majewski
On Mon, 15 Apr 2019, Lukasz Majewski wrote:

> +  if (! INTERNAL_SYSCALL_ERROR_P (r, err))
> +      valid_timespec_to_timespec64(&rem32, rem);

Missing space before '('.  Indentation in GNU style is two columns, so two
levels of indentation should be four columns (you have two columns
followed by six columns here).

--
Joseph S. Myers
[hidden email]
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 6/6] y2038: linux: Provide __clock_nanosleep64 implementation

Lukasz Majewski
Hi Joseph,

> On Mon, 15 Apr 2019, Lukasz Majewski wrote:
>
> > +  if (! INTERNAL_SYSCALL_ERROR_P (r, err))
> > +      valid_timespec_to_timespec64(&rem32, rem);  
>
> Missing space before '('.  Indentation in GNU style is two columns,
> so two levels of indentation should be four columns (you have two
> columns followed by six columns here).
>

Thanks for spotting. I will fix it in v2.


Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: [hidden email]

attachment0 (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Lukasz Majewski
In reply to this post by Joseph Myers
Hi Joseph,

> On Mon, 15 Apr 2019, Lukasz Majewski wrote:
>
> > The new 64 bit syscall (clock_settime64) available from Linux
> > 5.1+ has been used when applicable on 32 bit systems.
> > The execution path on 64 bit systems has not been changed or
> > affected in any way.  
>
> Is this unchanged specifically because of __TIMESIZE conditionals in
> the code (so that it doesn't matter whether the 64-bit systems define
> any new syscall names)?
I think yes.

When __TIMESIZE == 64 the clock_settime syscall is supporting 64 bit
time. The goal is to not change execution path for e.g. x86_64.
IMHO this shall be kept separate from Y2038 and 32 bit execution path.


This patch tries to follow 64 bit conversion for __clock_* functions as
presented in:
https://sourceware.org/glibc/wiki/Y2038ProofnessDesign#clock_gettime.28.29

__TIMESIZE is necessary to distinct the 32/64 bit case as
__clock_settime64 is used for both cases.

In this function I also need to check if __TIMESIZE is defined
https://github.com/lmajewski/y2038_glibc/commit/80f3b6220d80a8579e88b63fb90ba8ea7b771c5c#diff-725737619c471831ff796099fbab5c0aR33

as __clock_settime64 is aliased to clock_settime on 64 bit systems and
then aliased/linked to librt (for backward compatibility).

>
> Also, I don't see any __ASSUME_* / ENOSYS handling in this patch.  As
> usual, for any new syscall that might be used in code that could also
> use an older syscall:

I've followed some code pattern from glibc sources; for example:
sysdeps/unix/sysv/linux/lseek.c
Here the new syscall is only guarded by: # ifdef __NR__llseek
There was no __ASSUME_* for it.


However, the statx for example uses __ASSUME_STATX, which is defined
when kernel is newer than specified version. I will rewrite the code to
be similar to statx.

>
> * If the appropriate __ASSUME_* is defined, the new syscall can
> simply be used unconditionally.
>
> * If the appropriate __ASSUME_* is not defined, there needs to be
> runtime fallback to the old syscall if the new one returns an ENOSYS
> error (of course, that runtime fallback needs to check for the time
> overflowing the 32-bit range and give an appropriate error in that
> case, before calling the 32-bit syscall - and it's necessary to
> figure out what the priority should be of errors for invalid
> nanoseconds values versus overflowing seconds).  It is normal and
> expected for the kernel headers used to build glibc to be (much)
> newer than the oldest kernel version supported by the resulting glibc
> binaries at runtime.
Ach... I see your point. The __ASSUME_ prevents from nonfunctional glibc
when one uses new headers to compile glibc (which have
__NR__clock_settime64 defined) afterwards installed in a system with
old kernel (so __clock_settime64 returns -ENOSYS). I do agree that with
current patch there is no fallback to old syscall in that case.

I will fix it in v2.

>
> This __ASSUME_* macro definition in kernel-features.h would need a
> multi-paragraph comment discussing exactly what the semantics of such
> __ASSUME_* macros are in the context of the sets of syscall names and
> numbers present on different kinds of Linux kernel architectures.

Ok. I will add it to v2.

>
> > +#if __TIMESIZE != 64
> > +int
> > +__clock_settime (clockid_t clock_id, const struct timespec *tp)
> > +{
> > +  struct __timespec64 ts64;
> > +
> > +  if (! in_time_t_range (tp->tv_sec))
> > +    {
> > +      __set_errno (EOVERFLOW);
> > +      return -1;
> > +    }  
>
> I don't see how an overflow check makes sense in this context
> (converting a 32-bit timespec to a 64-bit one).
The above code is for the case when _TIME_BITS is not set (on 32
bit system) and we are after Y2038. The data provided by *tp has wrong
value and shall not be passed to the kernel (no matter if it supports
Y2038 time or not).

>




Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: [hidden email]

attachment0 (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Stepan Golosunov
In reply to this post by Lukasz Majewski
15.04.2019 в 00:08:38 +0200 Lukasz Majewski написал:
> +# if defined __NR_clock_settime64
> +  /* Make sure that passed __timespec64 struct pad is 0.  */
> +  struct __timespec64 ts = *tp;
> +  ts.tv_pad = 0;
> +  return INLINE_SYSCALL_CALL (clock_settime64, clock_id, &ts);

Isn't kernel supposed to zero out padding on its own?
At least comment in kernel's get_timespec64 says so:

        /* Zero out the padding for 32 bit systems or in compat mode */
        if (IS_ENABLED(CONFIG_64BIT_TIME) && in_compat_syscall())
                kts.tv_nsec &= 0xFFFFFFFFUL;

The code looks buggy though. It fails to zero out the padding in
32-bit kernels. That part is probably broken since
98f76206b3350 ("compat: Cleanup in_compat_syscall() callers").

And, hmm, is CONFIG_64BIT_TIME enabled anywhere?
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Lukasz Majewski
Hi Stepan,

> 15.04.2019 в 00:08:38 +0200 Lukasz Majewski написал:
> > +# if defined __NR_clock_settime64
> > +  /* Make sure that passed __timespec64 struct pad is 0.  */
> > +  struct __timespec64 ts = *tp;
> > +  ts.tv_pad = 0;
> > +  return INLINE_SYSCALL_CALL (clock_settime64, clock_id, &ts);  
>
> Isn't kernel supposed to zero out padding on its own?
> At least comment in kernel's get_timespec64 says so:
>
> /* Zero out the padding for 32 bit systems or in compat mode
> */ if (IS_ENABLED(CONFIG_64BIT_TIME) && in_compat_syscall())
> kts.tv_nsec &= 0xFFFFFFFFUL;
>
For ARM (and x86) 32 bit machines I do use following syscalls (like
clock_settime64):
https://elixir.bootlin.com/linux/v5.1-rc4/source/arch/arm/tools/syscall.tbl#L420

which are providing 64 bit time support on 32 bit systems.

Yes. In those systems the upper part (32 bits) of tv_nsec is cleared up
with mask in the kernel. However, I would prefer not to pass random data
to the kernel, and hence I do clear it up explicitly in glibc.

> The code looks buggy though. It fails to zero out the padding in
> 32-bit kernels.

For the 32 bit systems without Y2038 support enabled in glibc - the
clock_settime would be used, which corresponds to sys_clock_settime32()
in the kernel.

> That part is probably broken since
> 98f76206b3350 ("compat: Cleanup in_compat_syscall() callers").
>
> And, hmm, is CONFIG_64BIT_TIME enabled anywhere?

When I do use clock_settime64 on the glibc side (with _TIME_BITS=64), I
do not need to enable such config in the kernel.

If the kernel supports this call (5.1+), then use it, otherwise
fallback to clock_settime().

For 64 bit systems, I do not change the execution path.

If you are interested, please look on the following repo (which has
some more commits than those posted to the mailing list):
https://github.com/lmajewski/y2038_glibc/commits/Y2038-2.29-glibc-__clock-internal-struct-timespec-v1

And meta layer for testing.

https://github.com/lmajewski/meta-y2038

Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: [hidden email]

attachment0 (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Stepan Golosunov
20.04.2019 в 13:21:12 +0200 Lukasz Majewski написал:

> Hi Stepan,
>
> > 15.04.2019 в 00:08:38 +0200 Lukasz Majewski написал:
> > > +# if defined __NR_clock_settime64
> > > +  /* Make sure that passed __timespec64 struct pad is 0.  */
> > > +  struct __timespec64 ts = *tp;
> > > +  ts.tv_pad = 0;
> > > +  return INLINE_SYSCALL_CALL (clock_settime64, clock_id, &ts);  
> >
> > Isn't kernel supposed to zero out padding on its own?
> > At least comment in kernel's get_timespec64 says so:
> >
> > /* Zero out the padding for 32 bit systems or in compat mode
> > */ if (IS_ENABLED(CONFIG_64BIT_TIME) && in_compat_syscall())
> > kts.tv_nsec &= 0xFFFFFFFFUL;
> >
>
> For ARM (and x86) 32 bit machines I do use following syscalls (like
> clock_settime64):
> https://elixir.bootlin.com/linux/v5.1-rc4/source/arch/arm/tools/syscall.tbl#L420
>
> which are providing 64 bit time support on 32 bit systems.
>
> Yes. In those systems the upper part (32 bits) of tv_nsec is cleared up
> with mask in the kernel.

Is it? The kernel (5.1-rc6) code looks to me like

        /* Zero out the padding for 32 bit systems or in compat mode */
        if (false && false)
                kts.tv_nsec &= 0xFFFFFFFFUL;

in 32-bit kernels. And like

        if (false && true)
                kts.tv_nsec &= 0xFFFFFFFFUL;

for COMPAT syscalls in 64-bit kernels.

It should probably be changed into

        if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall())
                kts.tv_nsec &= 0xFFFFFFFFUL;

(Or into something like

        if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall() && !COMPAT_USE_64BIT_TIME)
                kts.tv_nsec &= 0xFFFFFFFFUL;

if x32 should retain 64-bit tv_nsec.)

> However, I would prefer not to pass random data
> to the kernel, and hence I do clear it up explicitly in glibc.

If the kernel does not ignore padding on its own, then zeroing it out
is required everywhere timespec is passed to kernel, including via
code not known to glibc. (Does anyone promise that there won't be any
ioctls that accept timespec, for example?) That seems to be
error-prone (and might requre copying larger structes).

On the other hand, if kernel 5.1+ ignores padding as intended there is
no need to create additional copy of structs in glibc code that calls
into clock_settime64 (or into timer_settime64 that accepts larger
struct, for example).

> > The code looks buggy though. It fails to zero out the padding in
> > 32-bit kernels.
>
> For the 32 bit systems without Y2038 support enabled in glibc - the
> clock_settime would be used, which corresponds to sys_clock_settime32()
> in the kernel.

I am talking about kernels with Y2038 support.

> > That part is probably broken since
> > 98f76206b3350 ("compat: Cleanup in_compat_syscall() callers").
> >
> > And, hmm, is CONFIG_64BIT_TIME enabled anywhere?

I guess that the remaining CONFIG_64BIT_TIME in kernel should be
replaced with CONFIG_COMPAT_32BIT_TIME or removed.
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Arnd Bergmann
On Mon, Apr 22, 2019 at 11:07 AM Stepan Golosunov
<[hidden email]> wrote:

> 20.04.2019 в 13:21:12 +0200 Lukasz Majewski написал:
> Is it? The kernel (5.1-rc6) code looks to me like
>
>         /* Zero out the padding for 32 bit systems or in compat mode */
>         if (false && false)
>                 kts.tv_nsec &= 0xFFFFFFFFUL;
>
> in 32-bit kernels. And like
>
>         if (false && true)
>                 kts.tv_nsec &= 0xFFFFFFFFUL;
>
> for COMPAT syscalls in 64-bit kernels.
>
> It should probably be changed into
>
>         if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall())
>                 kts.tv_nsec &= 0xFFFFFFFFUL;
>
> (Or into something like
>
>         if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall() && !COMPAT_USE_64BIT_TIME)
>                 kts.tv_nsec &= 0xFFFFFFFFUL;
>
> if x32 should retain 64-bit tv_nsec.)

I think the problem is that at some point CONFIG_64BIT_TIME was
meant to be enabled on both 32-bit and 64-bit kernels, but the
definition got changed along  the way.

We probably just want

        if (in_compat_syscall() )
               kts.tv_nsec &= 0xFFFFFFFFUL;

here, which would then truncate the nanoseconds for all compat
mode including x32. For native mode, we don't need to truncate
it, since timespec64 has a 32-bit 'tv_nsec' field in the kernel.

> > However, I would prefer not to pass random data
> > to the kernel, and hence I do clear it up explicitly in glibc.
>
> If the kernel does not ignore padding on its own, then zeroing it out
> is required everywhere timespec is passed to kernel, including via
> code not known to glibc. (Does anyone promise that there won't be any
> ioctls that accept timespec, for example?) That seems to be
> error-prone (and might requre copying larger structes).
>
> On the other hand, if kernel 5.1+ ignores padding as intended there is
> no need to create additional copy of structs in glibc code that calls
> into clock_settime64 (or into timer_settime64 that accepts larger
> struct, for example).

The intention is that the kernel ignores the padding. If you find
another place in the kernel that forget that, we should fix it.

> > > And, hmm, is CONFIG_64BIT_TIME enabled anywhere?
>
> I guess that the remaining CONFIG_64BIT_TIME in kernel should be
> replaced with CONFIG_COMPAT_32BIT_TIME or removed.

We should remove CONFIG_64BIT_TIME. CONFIG_COMPAT_32BIT_TIME
is still needed to identify architectures that don't have it, in
particular riscv32.

       Arnd
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 3/6] y2038: linux: Provide __clock_settime64 implementation

Lukasz Majewski
Hi Arnd and Stepan,

> On Mon, Apr 22, 2019 at 11:07 AM Stepan Golosunov
> <[hidden email]> wrote:
> > 20.04.2019 в 13:21:12 +0200 Lukasz Majewski написал:
> > Is it? The kernel (5.1-rc6) code looks to me like
> >
> >         /* Zero out the padding for 32 bit systems or in compat
> > mode */ if (false && false)
> >                 kts.tv_nsec &= 0xFFFFFFFFUL;
> >
> > in 32-bit kernels. And like
> >
> >         if (false && true)
> >                 kts.tv_nsec &= 0xFFFFFFFFUL;
> >
> > for COMPAT syscalls in 64-bit kernels.
> >
> > It should probably be changed into
> >
> >         if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall())
> >                 kts.tv_nsec &= 0xFFFFFFFFUL;
> >
> > (Or into something like
> >
> >         if (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall()
> > && !COMPAT_USE_64BIT_TIME) kts.tv_nsec &= 0xFFFFFFFFUL;
> >
> > if x32 should retain 64-bit tv_nsec.)  
>
> I think the problem is that at some point CONFIG_64BIT_TIME was
> meant to be enabled on both 32-bit and 64-bit kernels, but the
> definition got changed along  the way.
>
> We probably just want
>
>         if (in_compat_syscall() )
>                kts.tv_nsec &= 0xFFFFFFFFUL;
>
> here, which would then truncate the nanoseconds for all compat
> mode including x32. For native mode, we don't need to truncate
> it, since timespec64 has a 32-bit 'tv_nsec' field in the kernel.
>
> > > However, I would prefer not to pass random data
> > > to the kernel, and hence I do clear it up explicitly in glibc.  
> >
> > If the kernel does not ignore padding on its own, then zeroing it
> > out is required everywhere timespec is passed to kernel, including
> > via code not known to glibc. (Does anyone promise that there won't
> > be any ioctls that accept timespec, for example?) That seems to be
> > error-prone (and might requre copying larger structes).
> >
> > On the other hand, if kernel 5.1+ ignores padding as intended there
> > is no need to create additional copy of structs in glibc code that
> > calls into clock_settime64 (or into timer_settime64 that accepts
> > larger struct, for example).  
Ok, I think I see your point:

- As kernel is ignoring padding, there is no need to copy the structure
  and set the padding to 0.

However, in patch:
[PATCH 1/6] y2038: Introduce internal for glibc struct __timespec64

The internal (for glibc) structure has been introduced - it has 32 bit
tv_nsec and 32 bit padding. As it is passed to the kernel - the padding
can have random values and hence shall be zeroed before passing to the
kernel.

The rationale for 32 bit tv_nsec is to be as close as possible to what
is exported by glibc (64 bit tv_sec and 32 bit tv_nsec) for Y2038.

I'm now wondering if it would be better to have glibc internal struct
__timespec64 having both fields 64 bit (as it would be easier to pass
it to Linux).


>
> The intention is that the kernel ignores the padding. If you find
> another place in the kernel that forget that, we should fix it.
>

Thanks Arnd for clarification.

> > > > And, hmm, is CONFIG_64BIT_TIME enabled anywhere?  
> >
> > I guess that the remaining CONFIG_64BIT_TIME in kernel should be
> > replaced with CONFIG_COMPAT_32BIT_TIME or removed.  
>
> We should remove CONFIG_64BIT_TIME. CONFIG_COMPAT_32BIT_TIME
> is still needed to identify architectures that don't have it, in
> particular riscv32.
>
>        Arnd



Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: [hidden email]

attachment0 (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

[PATCH v2 0/7] y2038: Linux: Provide __clock_* functions supporting 64 bit time

Lukasz Majewski
In reply to this post by Lukasz Majewski
This patch set introduces the conversion of __clock_{settime|
gettime|getres|nanosleep} to explicit 64 bit struct __timespec64
arguments. As a result those functions are now Y2038 safe

To achieve the above goal - new, internal to glibc,
struct __timespec64 has been also introduced as well as
helper functions to handle time conversions on 32 bit systems.

The code from this patch set uses new clock_* syscalls (explicit
64 bits) from Linux kernel (5.1+) as well.

In glibc new __ASSUME_64BIT_TIME define has been introduced to
indicate if above syscalls are provided by the kernel.

This work is (loosely) based on a previous development/patches:
https://libc-alpha.sourceware.narkive.com/zniMOWui/rfc-patch-00-52-make-glibc-y2038-proof#post68

Github branch (including the y2038 conversion example):
https://github.com/lmajewski/y2038_glibc/commits/Y2038-2.29-glibc-__clock-internal-struct-timespec-v2

Those patches have been applied on top of master branch:
SHA1: c57afec0a9b318bb691e0f5fa4e9681cf30df7a4

Shall be used with provided meta-y2038 for development and testing:
https://github.com/lmajewski/meta-y2038

I've used guidelines from:
https://www.gnu.org/software/libc/manual/html_mono/libc.html
"D.2.1 64-bit time symbol handling in the GNU C Library"
to convert *clock_settime*.

and most notably from:
https://sourceware.org/glibc/wiki/Y2038ProofnessDesign#clock_gettime.28.29

Feedback and comments are more than welcome.


Lukasz Majewski (7):
  y2038: Introduce internal for glibc struct __timespec64
  y2038: Introduce __ASSUME_64BIT_TIME define
  y2038: Provide conversion helpers for struct __timespec64
  y2038: linux: Provide __clock_settime64 implementation
  y2038: linux: Provide __clock_getres64 implementation
  y2038: linux: Provide __clock_gettime64 implementation
  y2038: linux: Provide __clock_nanosleep64 implementation

 include/time.h                            | 157 ++++++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/clock_getres.c    |  39 +++++++-
 sysdeps/unix/sysv/linux/clock_gettime.c   |  39 +++++++-
 sysdeps/unix/sysv/linux/clock_nanosleep.c |  71 +++++++++++++-
 sysdeps/unix/sysv/linux/clock_settime.c   |  50 +++++++++-
 sysdeps/unix/sysv/linux/kernel-features.h |  14 +++
 6 files changed, 361 insertions(+), 9 deletions(-)

--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v2 1/7] y2038: Introduce internal for glibc struct __timespec64

Lukasz Majewski
This type is a glibc's type similar to struct timespec
but whose tv_sec field is a __time64_t rather than a time_t,
which makes it Y2038-proof and usable to pass between user
code and Y2038-proof kernel syscalls (e.g. clock_gettime()).

To support passing this structure to the kernel - the tv_pad,
32 bit padding field has been introduced. The placement of it
depends on endiannes of the SoC.

Tested on x86_64 and ARM.

* include/time.h: Add struct __timespec64 definition

---
Changes for v2:
- None
---
 include/time.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/include/time.h b/include/time.h
index 61dd9e180b..7540ac0f8b 100644
--- a/include/time.h
+++ b/include/time.h
@@ -3,6 +3,7 @@
 
 #ifndef _ISOMAC
 # include <bits/types/locale_t.h>
+# include <endian.h>
 
 extern __typeof (strftime_l) __strftime_l;
 libc_hidden_proto (__strftime_l)
@@ -58,6 +59,27 @@ extern time_t __mktime_internal (struct tm *__tp,
  long int *__offset) attribute_hidden;
 
 #if __TIMESIZE == 64
+# define __timespec64 timespec
+#else
+/* The glibc Y2038-proof struct __timespec64 structure for a time value.
+   To keep things Posix-ish, we keep the nanoseconds field a 32-bit
+   signed long, but since the Linux field is a 64-bit signed int, we
+   pad our tv_nsec with a 32-bit bitfield, which should always be 0. */
+
+struct __timespec64
+{
+  __time64_t tv_sec;         /* Seconds */
+# if BYTE_ORDER == BIG_ENDIAN
+  int tv_pad: 32;            /* Padding named for checking/setting */
+  __int32_t tv_nsec;         /* Nanoseconds */
+# else
+  __int32_t tv_nsec;         /* Nanoseconds */
+  int tv_pad: 32;            /* Padding named for checking/setting */
+# endif
+};
+#endif
+
+#if __TIMESIZE == 64
 # define __ctime64 ctime
 #else
 extern char *__ctime64 (const __time64_t *__timer) __THROW;
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v2 2/7] y2038: Introduce __ASSUME_64BIT_TIME define

Lukasz Majewski
In reply to this post by Lukasz Majewski
This define indicates if the Linux kernel (5.1+) provides 64 bit versions
of time related syscalls (e.g. clock_settime64, clock_nanosleep_time64).

Those syscalls are now available on actively supported Linux architectures
and most of all are providing Y2038 correct time on 32 bit systems.

* sysdeps/unix/sysv/linux/kernel-features.h: (__ASSUME_64BIT_TIME):
[__LINUX_KERNEL_VERSION >= 0x050100]: Define.

---
Changes for v2:
- New patch
---
 sysdeps/unix/sysv/linux/kernel-features.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index bc5c959f58..2dbe5ada4c 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -143,3 +143,17 @@
    */
 
 #define __ASSUME_CLONE_DEFAULT 1
+
+/* Support for 64 bit version of clock_* Linux syscalls.
+
+   Support for following time related (and Y2038 safe) syscalls has been added
+   in the 5.1 Linux kernel:
+
+   clock_gettime64 (nr. 403)
+   clock_settime64 (nr. 404)
+   clock_getres_time64 (nr. 406)
+   clock_nanosleep_time64 (nr. 407)
+  */
+#if __LINUX_KERNEL_VERSION >= 0x050100
+# define __ASSUME_64BIT_TIME 1
+#endif
--
2.11.0

Reply | Threaded
Open this post in threaded view
|

[PATCH v2 3/7] y2038: Provide conversion helpers for struct __timespec64

Lukasz Majewski
In reply to this post by Lukasz Majewski
Those functions allow easy conversion between Y2038 safe struct
 __timespec64 and other time related data structures.

An inline function has been added to clear padding of struct
__timespec64. This function shall be used when Y2038 safe system
passes the data to Linux kernel (as it expects upper 32 bits of
tv_nsec being zeroed).

Moreover, those functions are NOT compiled when one runs 64 bit
system (the __TIMESIZE == 64) and are used only for 32 bit
wrappers (like __clock_gettime).

* include/time.h (valid_timeval_to_timespec64): Add.
* include/time.h (valid_timespec_to_timespec64): Likewise.
* include/time.h (valid_timespec64_to_timespec): Likewise.
* include/time.h (valid_timespec64_to_timeval): Likewise.
* include/time.h (IS_VALID_NANOSECONDS): Likewise.
* include/time.h (timespec_to_timespec64): Likewise.
* include/time.h (timespec64_to_timespec): Likewise.
* include/time.h (timespec64_to_timeval): Likewise.
* include/time.h (timespec64_clear_padding): Likewise.

---
Changes for v2:
- Add timespec64_clear_padding function
---
 include/time.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/include/time.h b/include/time.h
index 7540ac0f8b..9827d2f045 100644
--- a/include/time.h
+++ b/include/time.h
@@ -3,6 +3,7 @@
 
 #ifndef _ISOMAC
 # include <bits/types/locale_t.h>
+# include <stdbool.h>
 # include <endian.h>
 
 extern __typeof (strftime_l) __strftime_l;
@@ -177,5 +178,106 @@ extern double __difftime (time_t time1, time_t time0);
    actual clock ID.  */
 #define CLOCK_IDFIELD_SIZE 3
 
+/* Check whether T fits in time_t.  */
+static inline bool
+in_time_t_range (__time64_t t)
+{
+  time_t s = t;
+  return s == t;
+}
+
+# if __TIMESIZE != 64
+/* Set to zero the struct __timespec64's tv_pad.  */
+static inline void
+timespec64_clear_padding (const struct __timespec64 *tp)
+{
+  ((struct __timespec64*)tp)->tv_pad = 0;
+}
+/* Convert a known valid struct timeval into a struct __timespec64.  */
+static inline void
+valid_timeval_to_timespec64 (const struct timeval *tv32,
+     struct __timespec64 *ts64)
+{
+  ts64->tv_sec = tv32->tv_sec;
+  ts64->tv_nsec = tv32->tv_usec * 1000;
+}
+
+/* Convert a known valid struct timespec into a struct __timespec64.  */
+static inline void
+valid_timespec_to_timespec64 (const struct timespec *ts32,
+      struct __timespec64 *ts64)
+{
+  ts64->tv_sec = ts32->tv_sec;
+  ts64->tv_nsec = ts32->tv_nsec;
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+}
+
+/* Convert a known valid struct __timespec64 into a struct timespec.  */
+static inline void
+valid_timespec64_to_timespec (const struct __timespec64 *ts64,
+      struct timespec *ts32)
+{
+  ts32->tv_sec = (time_t) ts64->tv_sec;
+  ts32->tv_nsec = ts64->tv_nsec;
+}
+
+/* Convert a known valid struct __timespec64 into a struct timeval.  */
+static inline void
+valid_timespec64_to_timeval (const struct __timespec64 *ts64,
+     struct timeval *tv32)
+{
+  tv32->tv_sec = (time_t) ts64->tv_sec;
+  tv32->tv_usec = ts64->tv_nsec / 1000;
+}
+
+/* Check if a value lies with the valid nanoseconds range.  */
+#define IS_VALID_NANOSECONDS(ns) ((ns) >= 0 && (ns) <= 999999999)
+
+/* Check and convert a struct timespec into a struct __timespec64.  */
+static inline bool
+timespec_to_timespec64 (const struct timespec *ts32,
+                        struct __timespec64 *ts64)
+{
+  /* Check that ts32 holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts32->tv_nsec))
+    return false;
+  /* All ts32 fields can fit in ts64, so copy them.  */
+  valid_timespec_to_timespec64 (ts32, ts64);
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timespec.  */
+static inline bool
+timespec64_to_timespec (const struct __timespec64 *ts64,
+                        struct timespec *ts32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! in_time_t_range (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in ts32, so copy them.  */
+  valid_timespec64_to_timespec (ts64, ts32);
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timeval.  */
+static inline bool
+timespec64_to_timeval (const struct __timespec64 *ts64,
+                       struct timeval *tv32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! in_time_t_range (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in tv32, so copy them.  */
+  valid_timespec64_to_timeval (ts64, tv32);
+  return true;
+}
+# endif
 #endif
 #endif
--
2.11.0

12345