[libc-commits] [libc] [libc] Fix remaining 32-bit time syscalls on 32-bit platforms (PR #203009)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Wed Jun 10 08:07:07 PDT 2026


https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/203009

After removing 32-bit time_t support, some time-related syscall wrappers were still passing 32-bit timespec structures directly to 64-bit time syscalls (like SYS_clock_nanosleep_time64, SYS_pselect6_time64, etc.). This caused stack corruption and invalid arguments on 32-bit platforms.

Commit 961d5b8ac7de fixed this for clock_gettime. This commit applies similar fixes to:
- clock_settime
- nanosleep (including correct handling of remaining time)
- select (pselect6)
- utimensat
- futex (wait timeout)

Also fixes a bug in select where a null timeout would incorrectly result in passing a zero timeout (polling) instead of blocking.

>From 759fed34dad2f18aa809bfb1addb5c0fc0142631 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Wed, 10 Jun 2026 11:13:29 +0100
Subject: [PATCH] [libc] Fix remaining 32-bit time syscalls on 32-bit platforms

After removing 32-bit time_t support, some time-related syscall wrappers
were still passing 32-bit timespec structures directly to 64-bit time
syscalls (like SYS_clock_nanosleep_time64, SYS_pselect6_time64, etc.).
This caused stack corruption and invalid arguments on 32-bit platforms.

Commit 961d5b8ac7de fixed this for clock_gettime. This commit applies
similar fixes to:
- clock_settime
- nanosleep (including correct handling of remaining time)
- select (pselect6)
- utimensat
- futex (wait timeout)

Also fixes a bug in select where a null timeout would incorrectly
result in passing a zero timeout (polling) instead of blocking.
---
 .../OSUtil/linux/syscall_wrappers/utimensat.h | 24 ++++++++++++++--
 .../src/__support/threads/linux/futex_utils.h | 22 ++++++++++++++-
 .../__support/time/linux/clock_settime.cpp    | 19 +++++++++++--
 libc/src/sys/select/linux/select.cpp          | 26 ++++++++++++++---
 libc/src/time/linux/nanosleep.cpp             | 28 +++++++++++++++++--
 5 files changed, 105 insertions(+), 14 deletions(-)

diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
index cbf79f5de2cbf..13dde485dbe1f 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/utimensat.h
@@ -15,15 +15,33 @@
 #include "src/__support/macros/config.h"
 
 #include "hdr/types/struct_timespec.h"
-#include <sys/syscall.h> // For syscall numbers
+#include <sys/syscall.h>
+#if defined(SYS_utimensat_time64)
+#include <linux/time_types.h>
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 namespace linux_syscalls {
 
 LIBC_INLINE ErrorOr<int> utimensat(int dirfd, const char *path,
-                                   const struct timespec times[2], int flags) {
+                                   const timespec times[2], int flags) {
 #if defined(SYS_utimensat_time64)
-  int ret = syscall_impl<int>(SYS_utimensat_time64, dirfd, path, times, flags);
+  int ret;
+  if constexpr (sizeof(timespec) == sizeof(__kernel_timespec)) {
+    ret = syscall_impl<int>(SYS_utimensat_time64, dirfd, path, times, flags);
+  } else {
+    if (times != nullptr) {
+      __kernel_timespec ts64[2];
+      ts64[0].tv_sec = times[0].tv_sec;
+      ts64[0].tv_nsec = times[0].tv_nsec;
+      ts64[1].tv_sec = times[1].tv_sec;
+      ts64[1].tv_nsec = times[1].tv_nsec;
+      ret = syscall_impl<int>(SYS_utimensat_time64, dirfd, path, ts64, flags);
+    } else {
+      ret =
+          syscall_impl<int>(SYS_utimensat_time64, dirfd, path, nullptr, flags);
+    }
+  }
 #elif defined(SYS_utimensat)
   static_assert(
       sizeof(timespec::tv_nsec) == sizeof(long),
diff --git a/libc/src/__support/threads/linux/futex_utils.h b/libc/src/__support/threads/linux/futex_utils.h
index ff6b5d526a3c1..b3b42e3308bc0 100644
--- a/libc/src/__support/threads/linux/futex_utils.h
+++ b/libc/src/__support/threads/linux/futex_utils.h
@@ -20,6 +20,9 @@
 #include "src/__support/time/abs_timeout.h"
 #include <linux/errno.h>
 #include <linux/futex.h>
+#if defined(SYS_futex_time64)
+#include <linux/time_types.h>
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -52,12 +55,29 @@ class Futex : public cpp::Atomic<FutexWordType> {
       if (this->load(cpp::MemoryOrder::RELAXED) != expected)
         return 0;
 
+#if defined(SYS_futex_time64)
+      __kernel_timespec ts64{};
+      void *timeout_arg = nullptr;
+      if (timeout) {
+        if constexpr (sizeof(timespec) == sizeof(__kernel_timespec)) {
+          timeout_arg = const_cast<timespec *>(&timeout->get_timespec());
+        } else {
+          ts64.tv_sec = timeout->get_timespec().tv_sec;
+          ts64.tv_nsec = timeout->get_timespec().tv_nsec;
+          timeout_arg = &ts64;
+        }
+      }
+#else
+      void *timeout_arg =
+          timeout ? const_cast<timespec *>(&timeout->get_timespec()) : nullptr;
+#endif
+
       int ret = syscall_impl<int>(
           /*syscall_number=*/FUTEX_SYSCALL_ID,
           /*futex_addr=*/this,
           /*op=*/op,
           /*expected=*/expected,
-          /*timeout=*/timeout ? &timeout->get_timespec() : nullptr,
+          /*timeout=*/timeout_arg,
           /*ignored=*/nullptr,
           /*bitset=*/FUTEX_BITSET_MATCH_ANY);
 
diff --git a/libc/src/__support/time/linux/clock_settime.cpp b/libc/src/__support/time/linux/clock_settime.cpp
index ec96828b42352..fdcc9eec290da 100644
--- a/libc/src/__support/time/linux/clock_settime.cpp
+++ b/libc/src/__support/time/linux/clock_settime.cpp
@@ -24,9 +24,22 @@ namespace internal {
 ErrorOr<int> clock_settime(clockid_t clockid, const timespec *ts) {
   int ret;
 #if defined(SYS_clock_settime64)
-  ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime64,
-                                          static_cast<long>(clockid),
-                                          reinterpret_cast<long>(ts));
+  if constexpr (sizeof(timespec) == sizeof(__kernel_timespec)) {
+    ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime64,
+                                            static_cast<long>(clockid),
+                                            reinterpret_cast<long>(ts));
+  } else {
+    __kernel_timespec ts64{};
+    __kernel_timespec *ts64_ptr = nullptr;
+    if (ts != nullptr) {
+      ts64.tv_sec = ts->tv_sec;
+      ts64.tv_nsec = ts->tv_nsec;
+      ts64_ptr = &ts64;
+    }
+    ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_settime64,
+                                            static_cast<long>(clockid),
+                                            reinterpret_cast<long>(ts64_ptr));
+  }
 #elif defined(SYS_clock_settime)
   static_assert(
       sizeof(timespec::tv_nsec) == sizeof(long),
diff --git a/libc/src/sys/select/linux/select.cpp b/libc/src/sys/select/linux/select.cpp
index 939cad4cd2bdd..f3add9cce3214 100644
--- a/libc/src/sys/select/linux/select.cpp
+++ b/libc/src/sys/select/linux/select.cpp
@@ -17,7 +17,10 @@
 #include "src/__support/macros/config.h"
 
 #include "hdr/types/size_t.h"
-#include <sys/syscall.h> // For syscall numbers.
+#include <sys/syscall.h>
+#if defined(SYS_pselect6_time64)
+#include <linux/time_types.h>
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -55,8 +58,22 @@ LLVM_LIBC_FUNCTION(int, select,
   }
   pselect6_sigset_t pss{nullptr, sizeof(sigset_t)};
 #if defined(SYS_pselect6_time64)
-  int ret = LIBC_NAMESPACE::syscall_impl<int>(
-      SYS_pselect6_time64, nfds, read_set, write_set, error_set, &ts, &pss);
+  int ret;
+  if constexpr (sizeof(timespec) == sizeof(__kernel_timespec)) {
+    ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pselect6_time64, nfds, read_set,
+                                            write_set, error_set,
+                                            timeout ? &ts : nullptr, &pss);
+  } else {
+    __kernel_timespec ts64{};
+    __kernel_timespec *ts_ptr = nullptr;
+    if (timeout != nullptr) {
+      ts64.tv_sec = ts.tv_sec;
+      ts64.tv_nsec = ts.tv_nsec;
+      ts_ptr = &ts64;
+    }
+    ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pselect6_time64, nfds, read_set,
+                                            write_set, error_set, ts_ptr, &pss);
+  }
 #elif defined(SYS_pselect6)
   static_assert(
       sizeof(timespec::tv_nsec) == sizeof(long),
@@ -64,7 +81,8 @@ LLVM_LIBC_FUNCTION(int, select,
       "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);
+                                              write_set, error_set,
+                                              timeout ? &ts : nullptr, &pss);
 #else
 #error "SYS_pselect6 and SYS_pselect6_time64 syscalls not available."
 #endif
diff --git a/libc/src/time/linux/nanosleep.cpp b/libc/src/time/linux/nanosleep.cpp
index 15b119ead20c6..0012512f38c7e 100644
--- a/libc/src/time/linux/nanosleep.cpp
+++ b/libc/src/time/linux/nanosleep.cpp
@@ -14,14 +14,36 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 
-#include <sys/syscall.h> // For syscall numbers.
+#include <sys/syscall.h>
+#if defined(SYS_clock_nanosleep_time64)
+#include <linux/time_types.h>
+#endif
 
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, nanosleep, (const timespec *req, timespec *rem)) {
 #if defined(SYS_clock_nanosleep_time64)
-  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_nanosleep_time64,
-                                              CLOCK_REALTIME, 0, req, rem);
+  int ret;
+  if constexpr (sizeof(timespec) == sizeof(__kernel_timespec)) {
+    ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_clock_nanosleep_time64,
+                                            CLOCK_REALTIME, 0, req, rem);
+  } else {
+    __kernel_timespec ts64_req{};
+    __kernel_timespec *req_ptr = nullptr;
+    if (req != nullptr) {
+      ts64_req.tv_sec = req->tv_sec;
+      ts64_req.tv_nsec = req->tv_nsec;
+      req_ptr = &ts64_req;
+    }
+    __kernel_timespec ts64_rem{};
+    __kernel_timespec *rem_ptr = rem ? &ts64_rem : nullptr;
+    ret = LIBC_NAMESPACE::syscall_impl<int>(
+        SYS_clock_nanosleep_time64, CLOCK_REALTIME, 0, req_ptr, rem_ptr);
+    if (ret == -EINTR && rem != nullptr) {
+      rem->tv_sec = static_cast<decltype(rem->tv_sec)>(ts64_rem.tv_sec);
+      rem->tv_nsec = static_cast<decltype(rem->tv_nsec)>(ts64_rem.tv_nsec);
+    }
+  }
 #elif defined(SYS_nanosleep)
   static_assert(
       sizeof(timespec::tv_nsec) == sizeof(long),



More information about the libc-commits mailing list