From patchwork Wed Apr 18 20:17:28 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Albert ARIBAUD X-Patchwork-Id: 26791 Received: (qmail 103309 invoked by alias); 18 Apr 2018 20:19:28 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 103209 invoked by uid 89); 18 Apr 2018 20:19:27 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.8 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_NUMSUBJECT, RCVD_IN_DNSWL_LOW, RCVD_IN_RP_RNBL autolearn=ham version=3.3.2 spammy=lies, meets X-HELO: smtp3-g21.free.fr From: "Albert ARIBAUD (3ADEV)" To: libc-alpha@sourceware.org Cc: "Albert ARIBAUD (3ADEV)" Subject: [[PATCH RFC 2] 12/63] Y2038: add struct __timespec64 Date: Wed, 18 Apr 2018 22:17:28 +0200 Message-Id: <20180418201819.15952-13-albert.aribaud@3adev.fr> In-Reply-To: <20180418201819.15952-12-albert.aribaud@3adev.fr> References: <20180418201819.15952-1-albert.aribaud@3adev.fr> <20180418201819.15952-2-albert.aribaud@3adev.fr> <20180418201819.15952-3-albert.aribaud@3adev.fr> <20180418201819.15952-4-albert.aribaud@3adev.fr> <20180418201819.15952-5-albert.aribaud@3adev.fr> <20180418201819.15952-6-albert.aribaud@3adev.fr> <20180418201819.15952-7-albert.aribaud@3adev.fr> <20180418201819.15952-8-albert.aribaud@3adev.fr> <20180418201819.15952-9-albert.aribaud@3adev.fr> <20180418201819.15952-10-albert.aribaud@3adev.fr> <20180418201819.15952-11-albert.aribaud@3adev.fr> <20180418201819.15952-12-albert.aribaud@3adev.fr> To be Y2038-proof, struct __timespec64 needs its tv_sec field to be a __time64_t rather than a __time_t. However, the question is which type should the tv_nsec field be. Keeping tv_nsec a long (32-bit) would be compatible with Posix requirements but would result in the GLIBC struct timespec being binary-incompatible with the Linux 64-bit struct timespec, which contains a 64-bit, not 32-bit, signed tv_nsec field. In order to maintain Posix compatibility yet simplify conversion between Posix and Linux struct timespec values, the Y2038-proof struct time stores its tv_nsec field as a 32-bit signed integer plus a padding which can serve as a 64-bit sign extension. This both meets Posix requirements and makes the GLIBC and Linux struct timespec binary compatible. Note that in the API (which is not modified here, and will be later alongside all Y2038-sensitive APIs), this padding is made 'invisible' by defining it as an anonymous bitfield, whereas the struct __timespec64 introduced here has a named field for the padding, allowing implementations to read and write it. Also, provide static inline functions and macros for checking and converting between 32-bit and 64-bit timespecs. --- include/time.h | 49 +++++++++++++++++++++++++++++++++++++++ time/bits/types/struct_timespec.h | 23 ++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/include/time.h b/include/time.h index d74f66e7c6..16286178e1 100644 --- a/include/time.h +++ b/include/time.h @@ -130,5 +130,54 @@ fits_in_time_t (__time64_t t) return t == (time_t) t; } +/* 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; +} + +/* 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 (! fits_in_time_t(ts64->tv_sec)) + return false; + /* all ts64 fields can fit in ts32, so copy them */ + valid_timespec64_to_timespec(ts64, ts32); + return true; +} #endif #endif diff --git a/time/bits/types/struct_timespec.h b/time/bits/types/struct_timespec.h index 644db9fdb6..7461ac0836 100644 --- a/time/bits/types/struct_timespec.h +++ b/time/bits/types/struct_timespec.h @@ -2,6 +2,7 @@ #define __timespec_defined 1 #include +#include /* POSIX.1b structure for a time value. This is like a `struct timeval' but has nanoseconds instead of microseconds. */ @@ -11,4 +12,26 @@ struct timespec __syscall_slong_t tv_nsec; /* Nanoseconds. */ }; +/* 64-bit time version. To keep tings Posix-ish, we keep the nanoseconds + field a signed long, but since Linux has a 64-bit signed int, we pad it + with a 32-bit int, which should always be 0. + Note that the public type has an anonymous bitfield as padding, so that + it cannot be written into (or read from). */ +#if BYTE_ORDER == BIG_ENDIAN +struct __timespec64 +{ + __time64_t tv_sec; /* Seconds */ + int tv_pad: 32; /* Padding named for checking/setting */ + __syscall_slong_t tv_nsec; /* Nanoseconds */ +}; +#else +struct __timespec64 +{ + __time64_t tv_sec; /* Seconds */ + __syscall_slong_t tv_nsec; /* Nanoseconds */ + int tv_pad: 32; /* Padding named for checking/setting */ +}; + +#endif + #endif