[libc-commits] [libc] [libc] Remove 32-bit time_t support (PR #200426)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Fri May 29 08:01:28 PDT 2026
https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/200426
Removed time_t_32.h and forced time_t to 64-bit (__INT64_TYPE__) on all platforms.
Forced tv_nsec in struct timespec and tv_usec in struct timeval to 64-bit. This ensures struct timespec matches the kernel layout of struct __kernel_timespec, allowing direct casting in syscall wrappers. Note that under C23, tv_nsec is allowed to be any signed integer type capable of representing [0, 999999999], making this 64-bit definition fully compliant.
Updated syscall wrappers to prefer 64-bit time syscalls:
* clock_gettime64
* clock_settime64
* clock_nanosleep_time64
* pselect6_time64
* utimensat_time64
* futex_time64
Added static_asserts to legacy fallback paths to prevent compiling on 32-bit platforms that lack 64-bit time support.
Removed obsolete Y2038 overflow tests from mktime and other time tests.
Assisted-by: Automated tooling, human reviewed.
>From 45027b3744cb5d6c6bc5460b063fea969c3a66d3 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 29 May 2026 13:17:53 +0100
Subject: [PATCH] [libc] Remove 32-bit time_t support
Removed time_t_32.h and forced time_t to 64-bit (__INT64_TYPE__) on all
platforms.
Forced tv_nsec in struct timespec and tv_usec in struct timeval to
64-bit. This ensures struct timespec matches the kernel layout of
struct __kernel_timespec, allowing direct casting in syscall wrappers.
Note that under C23, tv_nsec is allowed to be any signed integer type
capable of representing [0, 999999999], making this 64-bit definition
fully compliant.
Updated syscall wrappers to prefer 64-bit time syscalls:
* clock_gettime64
* clock_settime64
* clock_nanosleep_time64
* pselect6_time64
* utimensat_time64
* futex_time64
Added static_asserts to legacy fallback paths to prevent compiling on
32-bit platforms that lack 64-bit time support.
Removed obsolete Y2038 overflow tests from mktime and other time tests.
Assisted-by: Automated tooling, human reviewed.
---
.../modules/LLVMLibCCompileOptionRules.cmake | 4 -
libc/cmake/modules/LLVMLibCFlagRules.cmake | 13 ---
libc/config/config.json | 6 --
libc/include/llvm-libc-types/CMakeLists.txt | 6 +-
.../include/llvm-libc-types/struct_timespec.h | 5 +-
libc/include/llvm-libc-types/suseconds_t.h | 2 +-
libc/include/llvm-libc-types/time_t.h | 4 -
libc/include/llvm-libc-types/time_t_32.h | 20 -----
.../OSUtil/linux/syscall_wrappers/utimensat.h | 10 ++-
.../src/__support/threads/linux/futex_utils.h | 9 ++
libc/src/__support/threads/linux/futex_word.h | 6 +-
.../__support/time/linux/clock_gettime.cpp | 33 ++++---
.../__support/time/linux/clock_settime.cpp | 23 ++---
libc/src/poll/linux/poll.cpp | 15 ++--
.../src/sched/linux/sched_rr_get_interval.cpp | 31 ++-----
libc/src/sys/select/linux/select.cpp | 15 ++--
libc/src/sys/time/linux/getitimer.cpp | 20 +++--
libc/src/sys/time/linux/setitimer.cpp | 30 +++++--
libc/src/sys/time/linux/utimes.cpp | 16 +---
libc/src/time/linux/nanosleep.cpp | 14 +--
libc/src/time/time_constants.h | 6 --
libc/src/time/time_utils.cpp | 35 +-------
libc/test/src/time/asctime_test.cpp | 2 -
libc/test/src/time/gmtime_r_test.cpp | 2 -
libc/test/src/time/gmtime_test.cpp | 4 -
libc/test/src/time/mktime_test.cpp | 90 -------------------
26 files changed, 128 insertions(+), 293 deletions(-)
delete mode 100644 libc/include/llvm-libc-types/time_t_32.h
diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index c65fc4db605c9..02921973e4940 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -134,10 +134,6 @@ function(_get_compile_options_from_config output_var)
libc_add_definition(config_options "LIBC_COPT_USE_MEM_BUILTINS")
endif()
- if(LIBC_TYPES_TIME_T_IS_32_BIT AND LLVM_LIBC_FULL_BUILD)
- libc_add_definition(config_options "LIBC_TYPES_TIME_T_IS_32_BIT")
- endif()
-
if(LIBC_ADD_NULL_CHECKS)
libc_add_definition(config_options "LIBC_ADD_NULL_CHECKS")
endif()
diff --git a/libc/cmake/modules/LLVMLibCFlagRules.cmake b/libc/cmake/modules/LLVMLibCFlagRules.cmake
index d721756f9b4c5..70a3f9b64483e 100644
--- a/libc/cmake/modules/LLVMLibCFlagRules.cmake
+++ b/libc/cmake/modules/LLVMLibCFlagRules.cmake
@@ -308,16 +308,3 @@ if(NOT DEFINED SKIP_FLAG_EXPANSION_ROUND_OPT)
set(SKIP_FLAG_EXPANSION_ROUND_OPT TRUE)
endif()
endif()
-
-# Choose whether time_t is 32- or 64-bit, based on target architecture
-# and config options. This will be used to set a #define during the
-# library build, and also to select the right version of time_t.h for
-# the output headers.
-if(LIBC_TARGET_ARCHITECTURE_IS_ARM AND NOT (LIBC_CONF_TIME_64BIT))
- # Set time_t to 32 bit for compatibility with glibc, unless
- # configuration says otherwise
- set(LIBC_TYPES_TIME_T_IS_32_BIT TRUE)
-else()
- # Other platforms default to 64-bit time_t
- set(LIBC_TYPES_TIME_T_IS_32_BIT FALSE)
-endif()
diff --git a/libc/config/config.json b/libc/config/config.json
index 27d9f08ed31e0..510a6c1b1990c 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -167,12 +167,6 @@
"doc": "Make setjmp save the value of x18, and longjmp restore it. The AArch64 ABI delegates this register to platform ABIs, which can choose whether to make it caller-saved."
}
},
- "time": {
- "LIBC_CONF_TIME_64BIT": {
- "value": false,
- "doc": "Force the size of time_t to 64 bits, even on platforms where compatibility considerations would otherwise make it 32-bit."
- }
- },
"general": {
"LIBC_ADD_NULL_CHECKS": {
"value": true,
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 7c88e6e538879..6f3eca7e8e606 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -87,11 +87,7 @@ add_header(pthread_spinlock_t HDR pthread_spinlock_t.h DEPENDS .pid_t)
add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
add_header(pthread_id_np_t HDR pthread_id_np_t.h DEPENDS libc.include.llvm-libc-macros.stdint_macros)
add_header(rlim_t HDR rlim_t.h)
-if(LIBC_TYPES_TIME_T_IS_32_BIT)
- add_header(time_t HDR time_t_32.h DEST_HDR time_t.h)
-else()
- add_header(time_t HDR time_t_64.h DEST_HDR time_t.h)
-endif()
+add_header(time_t HDR time_t_64.h DEST_HDR time_t.h)
add_header(sighandler_t HDR sighandler_t.h)
add_header(stack_t HDR stack_t.h DEPENDS .size_t)
add_header(suseconds_t HDR suseconds_t.h)
diff --git a/libc/include/llvm-libc-types/struct_timespec.h b/libc/include/llvm-libc-types/struct_timespec.h
index 8993ecc7db8f0..7546795665dec 100644
--- a/libc/include/llvm-libc-types/struct_timespec.h
+++ b/libc/include/llvm-libc-types/struct_timespec.h
@@ -18,8 +18,9 @@
struct timespec {
time_t tv_sec; /* Seconds. */
- /* TODO: BIG_ENDIAN may require padding. */
- long tv_nsec; /* Nanoseconds. */
+ /* Nanoseconds. Forced to 64-bit to match __kernel_timespec layout (C23
+ * compliant). */
+ __INT64_TYPE__ tv_nsec;
};
#endif // __APPLE__
diff --git a/libc/include/llvm-libc-types/suseconds_t.h b/libc/include/llvm-libc-types/suseconds_t.h
index acc1822cb59e1..cd18b4f7cb889 100644
--- a/libc/include/llvm-libc-types/suseconds_t.h
+++ b/libc/include/llvm-libc-types/suseconds_t.h
@@ -19,7 +19,7 @@
// to ensure type compatibility and avoid redefinition errors.
#include <sys/_types/_suseconds_t.h>
#else
-typedef long suseconds_t;
+typedef __INT64_TYPE__ suseconds_t;
#endif // __APPLE__
#endif // LLVM_LIBC_TYPES_SUSECONDS_T_H
diff --git a/libc/include/llvm-libc-types/time_t.h b/libc/include/llvm-libc-types/time_t.h
index 76920dc07ec69..7609a7a5c33f7 100644
--- a/libc/include/llvm-libc-types/time_t.h
+++ b/libc/include/llvm-libc-types/time_t.h
@@ -9,10 +9,6 @@
#ifndef LLVM_LIBC_TYPES_TIME_T_H
#define LLVM_LIBC_TYPES_TIME_T_H
-#ifdef LIBC_TYPES_TIME_T_IS_32_BIT
-#include "time_t_32.h"
-#else
#include "time_t_64.h"
-#endif
#endif // LLVM_LIBC_TYPES_TIME_T_H
diff --git a/libc/include/llvm-libc-types/time_t_32.h b/libc/include/llvm-libc-types/time_t_32.h
deleted file mode 100644
index 8d7a81e5ce7f7..0000000000000
--- a/libc/include/llvm-libc-types/time_t_32.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//===-- Definition of the type time_t -------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_TYPES_TIME_T_32_H
-#define LLVM_LIBC_TYPES_TIME_T_32_H
-
-#if defined(__APPLE__)
-// Darwin provides its own definition for time_t. Include it directly
-// to ensure type compatibility and avoid redefinition errors.
-#include <sys/_types/_time_t.h>
-#else
-typedef __INT32_TYPE__ time_t;
-#endif // __APPLE__
-
-#endif // LLVM_LIBC_TYPES_TIME_T_32_H
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
index d71afb9f5251d..cbf79f5de2cbf 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
@@ -23,14 +23,18 @@ namespace linux_syscalls {
LIBC_INLINE ErrorOr<int> utimensat(int dirfd, const char *path,
const struct timespec times[2], int flags) {
#if defined(SYS_utimensat_time64)
- constexpr auto UTIMENSAT_SYSCALL_ID = SYS_utimensat_time64;
+ int ret = syscall_impl<int>(SYS_utimensat_time64, dirfd, path, times, flags);
#elif defined(SYS_utimensat)
- constexpr auto UTIMENSAT_SYSCALL_ID = SYS_utimensat;
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ int ret = syscall_impl<int>(SYS_utimensat, dirfd, path, times, flags);
#else
#error "utimensat or utimensat_time64 syscalls not available."
#endif
- int ret = syscall_impl<int>(UTIMENSAT_SYSCALL_ID, dirfd, path, times, flags);
if (ret < 0)
return Error(-static_cast<int>(ret));
return ret;
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index 4a39c66c6eb9a..ff6b5d526a3c1 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -22,6 +22,15 @@
#include <linux/futex.h>
namespace LIBC_NAMESPACE_DECL {
+
+#if !defined(SYS_futex_time64) && defined(SYS_futex)
+static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+#endif
+
class Futex : public cpp::Atomic<FutexWordType> {
public:
using Timeout = internal::AbsTimeout;
diff --git a/libc/src/__support/threads/linux/futex_word.h b/libc/src/__support/threads/linux/futex_word.h
index 1cf7befea5c88..9ef55ceaeb790 100644
--- a/libc/src/__support/threads/linux/futex_word.h
+++ b/libc/src/__support/threads/linux/futex_word.h
@@ -17,10 +17,10 @@ namespace LIBC_NAMESPACE_DECL {
// Futexes are 32 bits in size on all platforms, including 64-bit platforms.
using FutexWordType = uint32_t;
-#if SYS_futex
-constexpr auto FUTEX_SYSCALL_ID = SYS_futex;
-#elif defined(SYS_futex_time64)
+#if defined(SYS_futex_time64)
constexpr auto FUTEX_SYSCALL_ID = SYS_futex_time64;
+#elif defined(SYS_futex)
+constexpr auto FUTEX_SYSCALL_ID = SYS_futex;
#else
#error "futex and futex_time64 syscalls not available."
#endif
diff --git a/libc/src/__support/time/linux/clock_gettime.cpp b/libc/src/__support/time/linux/clock_gettime.cpp
index 944fc0a2b80fe..c893bcf9b772e 100644
--- a/libc/src/__support/time/linux/clock_gettime.cpp
+++ b/libc/src/__support/time/linux/clock_gettime.cpp
@@ -25,7 +25,21 @@ namespace internal {
ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
using namespace vdso;
int ret;
-#if defined(SYS_clock_gettime)
+#if defined(SYS_clock_gettime64)
+ TypedSymbol<VDSOSym::ClockGetTime64> clock_gettime64;
+ if (LIBC_LIKELY(clock_gettime64 != nullptr)) {
+ ret = clock_gettime64(clockid, reinterpret_cast<__kernel_timespec *>(ts));
+ } else {
+ ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
+ static_cast<long>(clockid),
+ reinterpret_cast<long>(ts));
+ }
+#elif defined(SYS_clock_gettime)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
TypedSymbol<VDSOSym::ClockGetTime> clock_gettime;
if (LIBC_LIKELY(clock_gettime != nullptr))
ret = clock_gettime(clockid, ts);
@@ -33,23 +47,6 @@ ErrorOr<int> clock_gettime(clockid_t clockid, timespec *ts) {
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime,
static_cast<long>(clockid),
reinterpret_cast<long>(ts));
-#elif defined(SYS_clock_gettime64)
- static_assert(
- sizeof(time_t) == sizeof(int64_t),
- "SYS_clock_gettime64 requires struct timespec with 64-bit members.");
-
- TypedSymbol<VDSOSym::ClockGetTime64> clock_gettime64;
- __kernel_timespec ts64{};
- if (LIBC_LIKELY(clock_gettime64 != nullptr))
- ret = clock_gettime64(clockid, &ts64);
- else
- ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_gettime64,
- static_cast<long>(clockid),
- reinterpret_cast<long>(&ts64));
- if (ret == 0) {
- ts->tv_sec = static_cast<decltype(ts->tv_sec)>(ts64.tv_sec);
- ts->tv_nsec = static_cast<decltype(ts->tv_nsec)>(ts64.tv_nsec);
- }
#else
#error "SYS_clock_gettime and SYS_clock_gettime64 syscalls not available."
#endif
diff --git a/libc/src/__support/time/linux/clock_settime.cpp b/libc/src/__support/time/linux/clock_settime.cpp
index dd42610adb031..ec96828b42352 100644
--- a/libc/src/__support/time/linux/clock_settime.cpp
+++ b/libc/src/__support/time/linux/clock_settime.cpp
@@ -23,24 +23,19 @@ namespace LIBC_NAMESPACE_DECL {
namespace internal {
ErrorOr<int> clock_settime(clockid_t clockid, const timespec *ts) {
int ret;
-#if defined(SYS_clock_settime)
- ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime,
+#if defined(SYS_clock_settime64)
+ ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime64,
static_cast<long>(clockid),
reinterpret_cast<long>(ts));
-#elif defined(SYS_clock_settime64)
+#elif defined(SYS_clock_settime)
static_assert(
- sizeof(time_t) == sizeof(int64_t),
- "SYS_clock_settime64 requires struct timespec with 64-bit members.");
-
- __kernel_timespec ts64{};
-
- // Populate the 64-bit kernel structure from the user-provided timespec
- ts64.tv_sec = static_cast<decltype(ts64.tv_sec)>(ts->tv_sec);
- ts64.tv_nsec = static_cast<decltype(ts64.tv_nsec)>(ts->tv_nsec);
-
- ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime64,
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime,
static_cast<long>(clockid),
- reinterpret_cast<long>(&ts64));
+ reinterpret_cast<long>(ts));
#else
#error "SYS_clock_settime and SYS_clock_settime64 syscalls not available."
#endif
diff --git a/libc/src/poll/linux/poll.cpp b/libc/src/poll/linux/poll.cpp
index 4cac75b9687c8..7db5b16b1f5b9 100644
--- a/libc/src/poll/linux/poll.cpp
+++ b/libc/src/poll/linux/poll.cpp
@@ -16,7 +16,7 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
-#include <sys/syscall.h> // SYS_poll, SYS_ppoll
+#include <sys/syscall.h> // SYS_poll, SYS_ppoll, SYS_ppoll_time64
namespace LIBC_NAMESPACE_DECL {
@@ -34,12 +34,17 @@ LLVM_LIBC_FUNCTION(int, poll, (pollfd * fds, nfds_t nfds, int timeout)) {
} else {
tsp = nullptr;
}
-#if defined(SYS_ppoll)
- ret =
- LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll, fds, nfds, tsp, nullptr, 0);
-#elif defined(SYS_ppoll_time64)
+#if defined(SYS_ppoll_time64)
ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll_time64, fds, nfds, tsp,
nullptr, 0);
+#elif defined(SYS_ppoll)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ ret =
+ LIBC_NAMESPACE::syscall_impl<int>(SYS_ppoll, fds, nfds, tsp, nullptr, 0);
#else
#error "poll, ppoll, ppoll_time64 syscalls not available."
#endif // defined(SYS_ppoll) || defined(SYS_ppoll_time64)
diff --git a/libc/src/sched/linux/sched_rr_get_interval.cpp b/libc/src/sched/linux/sched_rr_get_interval.cpp
index eecbaa4dc03ce..c87d1808bc902 100644
--- a/libc/src/sched/linux/sched_rr_get_interval.cpp
+++ b/libc/src/sched/linux/sched_rr_get_interval.cpp
@@ -17,34 +17,21 @@
#include "hdr/types/struct_timespec.h"
#include <sys/syscall.h> // For syscall numbers.
-#ifdef SYS_sched_rr_get_interval_time64
-#include <linux/time_types.h> // For __kernel_timespec.
-#endif
-
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, sched_rr_get_interval,
(pid_t tid, struct timespec *tp)) {
-#ifdef SYS_sched_rr_get_interval
+#if defined(SYS_sched_rr_get_interval_time64)
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_sched_rr_get_interval_time64,
+ tid, tp);
+#elif defined(SYS_sched_rr_get_interval)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
int ret =
LIBC_NAMESPACE::syscall_impl<int>(SYS_sched_rr_get_interval, tid, tp);
-#elif defined(SYS_sched_rr_get_interval_time64)
- // The difference between the and SYS_sched_rr_get_interval
- // SYS_sched_rr_get_interval_time64 syscalls is the data type used for the
- // time interval parameter: the latter takes a struct __kernel_timespec
- int ret;
- if (tp) {
- struct __kernel_timespec ts32;
- ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_sched_rr_get_interval_time64,
- tid, &ts32);
- if (ret == 0) {
- tp->tv_sec = ts32.tv_sec;
- tp->tv_nsec = static_cast<long int>(ts32.tv_nsec);
- }
- } else
- // When tp is a nullptr, we still do the syscall to set ret and errno
- ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_sched_rr_get_interval_time64,
- tid, nullptr);
#else
#error \
"sched_rr_get_interval and sched_rr_get_interval_time64 syscalls not available."
diff --git a/libc/src/sys/select/linux/select.cpp b/libc/src/sys/select/linux/select.cpp
index 6c434eb584596..939cad4cd2bdd 100644
--- a/libc/src/sys/select/linux/select.cpp
+++ b/libc/src/sys/select/linux/select.cpp
@@ -16,7 +16,7 @@
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
-#include <stddef.h> // For size_t
+#include "hdr/types/size_t.h"
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
@@ -54,12 +54,17 @@ LLVM_LIBC_FUNCTION(int, select,
}
}
pselect6_sigset_t pss{nullptr, sizeof(sigset_t)};
-#if SYS_pselect6
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pselect6, nfds, read_set,
- write_set, error_set, &ts, &pss);
-#elif defined(SYS_pselect6_time64)
+#if defined(SYS_pselect6_time64)
int ret = LIBC_NAMESPACE::syscall_impl<int>(
SYS_pselect6_time64, nfds, read_set, write_set, error_set, &ts, &pss);
+#elif defined(SYS_pselect6)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pselect6, nfds, read_set,
+ write_set, error_set, &ts, &pss);
#else
#error "SYS_pselect6 and SYS_pselect6_time64 syscalls not available."
#endif
diff --git a/libc/src/sys/time/linux/getitimer.cpp b/libc/src/sys/time/linux/getitimer.cpp
index b874066796940..2a40491bc95fb 100644
--- a/libc/src/sys/time/linux/getitimer.cpp
+++ b/libc/src/sys/time/linux/getitimer.cpp
@@ -19,14 +19,18 @@ LLVM_LIBC_FUNCTION(int, getitimer, (int which, struct itimerval *curr_value)) {
long ret = 0;
if constexpr (sizeof(time_t) > sizeof(long)) {
// There is no SYS_getitimer_time64 call, so we can't use time_t directly.
- long curr_value32[4];
- ret =
- LIBC_NAMESPACE::syscall_impl<long>(SYS_getitimer, which, curr_value32);
- if (!ret) {
- curr_value->it_interval.tv_sec = curr_value32[0];
- curr_value->it_interval.tv_usec = curr_value32[1];
- curr_value->it_value.tv_sec = curr_value32[2];
- curr_value->it_value.tv_usec = curr_value32[3];
+ if (curr_value) {
+ long curr_value32[4];
+ ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_getitimer, which,
+ curr_value32);
+ if (!ret) {
+ curr_value->it_interval.tv_sec = curr_value32[0];
+ curr_value->it_interval.tv_usec = curr_value32[1];
+ curr_value->it_value.tv_sec = curr_value32[2];
+ curr_value->it_value.tv_usec = curr_value32[3];
+ }
+ } else {
+ ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_getitimer, which, nullptr);
}
} else {
ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_getitimer, which, curr_value);
diff --git a/libc/src/sys/time/linux/setitimer.cpp b/libc/src/sys/time/linux/setitimer.cpp
index fb163586e30d9..2c2f0d7198204 100644
--- a/libc/src/sys/time/linux/setitimer.cpp
+++ b/libc/src/sys/time/linux/setitimer.cpp
@@ -6,7 +6,9 @@
//
//===----------------------------------------------------------------------===//
#include "src/sys/time/setitimer.h"
+#include "hdr/errno_macros.h"
#include "hdr/types/struct_itimerval.h"
+#include "src/__support/CPP/limits.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
@@ -21,14 +23,30 @@ LLVM_LIBC_FUNCTION(int, setitimer,
if constexpr (sizeof(time_t) > sizeof(long)) {
// There is no SYS_setitimer_time64 call, so we can't use time_t directly,
// and need to convert it to long first.
- long new_value32[4] = {static_cast<long>(new_value->it_interval.tv_sec),
- static_cast<long>(new_value->it_interval.tv_usec),
- static_cast<long>(new_value->it_value.tv_sec),
- static_cast<long>(new_value->it_value.tv_usec)};
long old_value32[4];
+ long *old_value32_ptr = old_value ? old_value32 : nullptr;
- ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_setitimer, which, new_value32,
- old_value32);
+ if (new_value) {
+ // Check for overflow before casting to 32-bit long.
+ if (new_value->it_interval.tv_sec > cpp::numeric_limits<long>::max() ||
+ new_value->it_interval.tv_sec < cpp::numeric_limits<long>::min() ||
+ new_value->it_value.tv_sec > cpp::numeric_limits<long>::max() ||
+ new_value->it_value.tv_sec < cpp::numeric_limits<long>::min()) {
+ libc_errno = EOVERFLOW;
+ return -1;
+ }
+
+ long new_value32[4] = {static_cast<long>(new_value->it_interval.tv_sec),
+ static_cast<long>(new_value->it_interval.tv_usec),
+ static_cast<long>(new_value->it_value.tv_sec),
+ static_cast<long>(new_value->it_value.tv_usec)};
+
+ ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_setitimer, which,
+ new_value32, old_value32_ptr);
+ } else {
+ ret = LIBC_NAMESPACE::syscall_impl<long>(SYS_setitimer, which, nullptr,
+ old_value32_ptr);
+ }
if (!ret && old_value) {
old_value->it_interval.tv_sec = old_value32[0];
diff --git a/libc/src/sys/time/linux/utimes.cpp b/libc/src/sys/time/linux/utimes.cpp
index 1603f70f8f4db..abb56183cf7c3 100644
--- a/libc/src/sys/time/linux/utimes.cpp
+++ b/libc/src/sys/time/linux/utimes.cpp
@@ -22,16 +22,7 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, utimes,
(const char *path, const struct timeval times[2])) {
-#ifdef SYS_utimes
- // No need to define a timespec struct, use the syscall directly.
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_utimes, path, times);
-
- if (ret < 0) {
- libc_errno = -ret;
- return -1;
- }
- return 0;
-#elif defined(SYS_utimensat) || defined(SYS_utimensat_time64)
+#if defined(SYS_utimensat) || defined(SYS_utimensat_time64)
// the utimensat syscall requires a timespec struct, not timeval.
struct timespec ts[2];
@@ -73,9 +64,8 @@ LLVM_LIBC_FUNCTION(int, utimes,
}
return 0;
-
#else
-#error "utimes, utimensat, utimensat_time64, syscalls not available."
-#endif // SYS_utimensat
+#error "utimensat or utimensat_time64 syscalls not available."
+#endif // SYS_utimensat || SYS_utimensat_time64
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/linux/nanosleep.cpp b/libc/src/time/linux/nanosleep.cpp
index a30b97de40492..15b119ead20c6 100644
--- a/libc/src/time/linux/nanosleep.cpp
+++ b/libc/src/time/linux/nanosleep.cpp
@@ -19,14 +19,16 @@
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(int, nanosleep, (const timespec *req, timespec *rem)) {
-#if SYS_nanosleep
- int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_nanosleep, req, rem);
-#elif defined(SYS_clock_nanosleep_time64)
- static_assert(
- sizeof(time_t) == sizeof(int64_t),
- "SYS_clock_gettime64 requires struct timespec with 64-bit members.");
+#if defined(SYS_clock_nanosleep_time64)
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_nanosleep_time64,
CLOCK_REALTIME, 0, req, rem);
+#elif defined(SYS_nanosleep)
+ static_assert(
+ sizeof(timespec::tv_nsec) == sizeof(long),
+ "This legacy syscall fallback is only safe on platforms where tv_nsec "
+ "matches the register size (long). It is unsafe on 32-bit platforms "
+ "with 64-bit tv_nsec.");
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_nanosleep, req, rem);
#else
#error "SYS_nanosleep and SYS_clock_nanosleep_time64 syscalls not available."
#endif
diff --git a/libc/src/time/time_constants.h b/libc/src/time/time_constants.h
index 32eb0a171f8d1..078c427990a64 100644
--- a/libc/src/time/time_constants.h
+++ b/libc/src/time/time_constants.h
@@ -85,12 +85,6 @@ constexpr int DAYS_PER100_YEARS =
(DAYS_PER_NON_LEAP_YEAR * 100) + (100 / 4) - 1;
constexpr int DAYS_PER4_YEARS = (DAYS_PER_NON_LEAP_YEAR * 4) + 1;
-// The latest time that can be represented in this form is 03:14:07 UTC on
-// Tuesday, 19 January 2038 (corresponding to 2,147,483,647 seconds since the
-// start of the epoch). This means that systems using a 32-bit time_t type are
-// susceptible to the Year 2038 problem.
-constexpr int END_OF32_BIT_EPOCH_YEAR = 2038;
-
constexpr time_t OUT_OF_RANGE_RETURN_VALUE = -1;
constexpr cpp::array<cpp::string_view, DAYS_PER_WEEK> WEEK_DAY_NAMES = {
diff --git a/libc/src/time/time_utils.cpp b/libc/src/time/time_utils.cpp
index 1d0daea6b321e..d749ff83019c5 100644
--- a/libc/src/time/time_utils.cpp
+++ b/libc/src/time/time_utils.cpp
@@ -22,29 +22,6 @@ cpp::optional<time_t> mktime_internal(const tm *tm_out) {
// TODO(rtenneti); Handle leap seconds.
int64_t tm_year_from_base = tm_out->tm_year + time_constants::TIME_YEAR_BASE;
- // 32-bit end-of-the-world is 03:14:07 UTC on 19 January 2038.
- if (sizeof(time_t) == 4 &&
- tm_year_from_base >= time_constants::END_OF32_BIT_EPOCH_YEAR) {
- if (tm_year_from_base > time_constants::END_OF32_BIT_EPOCH_YEAR)
- return cpp::nullopt;
- if (tm_out->tm_mon > 0)
- return cpp::nullopt;
- if (tm_out->tm_mday > 19)
- return cpp::nullopt;
- else if (tm_out->tm_mday == 19) {
- if (tm_out->tm_hour > 3)
- return cpp::nullopt;
- else if (tm_out->tm_hour == 3) {
- if (tm_out->tm_min > 14)
- return cpp::nullopt;
- else if (tm_out->tm_min == 14) {
- if (tm_out->tm_sec > 7)
- return cpp::nullopt;
- }
- }
- }
- }
-
// Years are ints. A 32-bit year will fit into a 64-bit time_t.
// A 64-bit year will not.
static_assert(
@@ -141,15 +118,11 @@ int64_t update_from_seconds(time_t total_seconds, tm *tm) {
30, 31, 30, 31, 31, 29};
constexpr time_t time_min =
- (sizeof(time_t) == 4)
- ? INT_MIN
- : INT_MIN * static_cast<int64_t>(
- time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
+ INT_MIN *
+ static_cast<int64_t>(time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
constexpr time_t time_max =
- (sizeof(time_t) == 4)
- ? INT_MAX
- : INT_MAX * static_cast<int64_t>(
- time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
+ INT_MAX *
+ static_cast<int64_t>(time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR);
if (total_seconds < time_min || total_seconds > time_max)
return time_utils::out_of_range();
diff --git a/libc/test/src/time/asctime_test.cpp b/libc/test/src/time/asctime_test.cpp
index 2868bdec79a6c..7635fbb8dc62e 100644
--- a/libc/test/src/time/asctime_test.cpp
+++ b/libc/test/src/time/asctime_test.cpp
@@ -185,8 +185,6 @@ TEST_F(LlvmLibcAsctime, EndOf32BitEpochYear) {
}
TEST_F(LlvmLibcAsctime, Max64BitYear) {
- if (sizeof(time_t) == 4)
- return;
// Mon Jan 1 12:50:50 2170 (200 years from 1970),
struct tm tm_data;
char *result;
diff --git a/libc/test/src/time/gmtime_r_test.cpp b/libc/test/src/time/gmtime_r_test.cpp
index 700333519f3bd..8026d37618868 100644
--- a/libc/test/src/time/gmtime_r_test.cpp
+++ b/libc/test/src/time/gmtime_r_test.cpp
@@ -38,8 +38,6 @@ TEST_F(LlvmLibcGmTimeR, EndOf32BitEpochYear) {
}
TEST_F(LlvmLibcGmTimeR, Max64BitYear) {
- if (sizeof(time_t) == 4)
- return;
// Test for Tue Jan 1 12:50:50 in 2,147,483,647th year.
time_t seconds = 67767976202043050;
struct tm tm_data;
diff --git a/libc/test/src/time/gmtime_test.cpp b/libc/test/src/time/gmtime_test.cpp
index 1df768ac721fd..6e28e5987fde5 100644
--- a/libc/test/src/time/gmtime_test.cpp
+++ b/libc/test/src/time/gmtime_test.cpp
@@ -18,8 +18,6 @@
using LlvmLibcGmTime = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
TEST_F(LlvmLibcGmTime, OutOfRange) {
- if (sizeof(time_t) < sizeof(int64_t))
- return;
time_t seconds =
1 +
INT_MAX *
@@ -275,8 +273,6 @@ TEST_F(LlvmLibcGmTime, EndOf32BitEpochYear) {
}
TEST_F(LlvmLibcGmTime, Max64BitYear) {
- if (sizeof(time_t) == 4)
- return;
// Mon Jan 1 12:50:50 2170 (200 years from 1970),
time_t seconds = 6311479850;
struct tm *tm_data = LIBC_NAMESPACE::gmtime(&seconds);
diff --git a/libc/test/src/time/mktime_test.cpp b/libc/test/src/time/mktime_test.cpp
index 97901a8a35a38..6927ec935b85a 100644
--- a/libc/test/src/time/mktime_test.cpp
+++ b/libc/test/src/time/mktime_test.cpp
@@ -227,94 +227,6 @@ TEST(LlvmLibcMkTime, InvalidYear) {
tm_data);
}
-TEST(LlvmLibcMkTime, InvalidEndOf32BitEpochYear) {
- if (sizeof(time_t) != 4)
- return;
- {
- // 2038-01-19 03:14:08 tests overflow of the second in 2038.
- struct tm tm_data{.tm_sec = 8,
- .tm_min = 14,
- .tm_hour = 3,
- .tm_mday = 19,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-
- {
- // 2038-01-19 03:15:07 tests overflow of the minute in 2038.
- struct tm tm_data{.tm_sec = 7,
- .tm_min = 15,
- .tm_hour = 3,
- .tm_mday = 19,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-
- {
- // 2038-01-19 04:14:07 tests overflow of the hour in 2038.
- struct tm tm_data{.tm_sec = 7,
- .tm_min = 14,
- .tm_hour = 4,
- .tm_mday = 19,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-
- {
- // 2038-01-20 03:14:07 tests overflow of the day in 2038.
- struct tm tm_data{.tm_sec = 7,
- .tm_min = 14,
- .tm_hour = 3,
- .tm_mday = 20,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-
- {
- // 2038-02-19 03:14:07 tests overflow of the month in 2038.
- struct tm tm_data{.tm_sec = 7,
- .tm_min = 14,
- .tm_hour = 3,
- .tm_mday = 19,
- .tm_mon = Month::FEBRUARY,
- .tm_year = tm_year(2038),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-
- {
- // 2039-01-19 03:14:07 tests overflow of the year.
- struct tm tm_data{.tm_sec = 7,
- .tm_min = 14,
- .tm_hour = 3,
- .tm_mday = 19,
- .tm_mon = Month::JANUARY,
- .tm_year = tm_year(2039),
- .tm_wday = 0,
- .tm_yday = 0,
- .tm_isdst = 0};
- EXPECT_THAT(LIBC_NAMESPACE::mktime(&tm_data), Fails<time_t>(EOVERFLOW));
- }
-}
-
TEST(LlvmLibcMkTime, InvalidMonths) {
{
// -1 month from 1970-01-01 00:00:00 returns 1969-12-01 00:00:00.
@@ -621,8 +533,6 @@ TEST(LlvmLibcMkTime, EndOf32BitEpochYear) {
}
TEST(LlvmLibcMkTime, Max64BitYear) {
- if (sizeof(time_t) == 4)
- return;
{
// Mon Jan 1 12:50:50 2170 (200 years from 1970),
struct tm tm_data{.tm_sec = 50,
More information about the libc-commits
mailing list