From c0d58db0c0f180df38137421604f86d0fb96deab Mon Sep 17 00:00:00 2001 From: Oleg Lyovin Date: Tue, 2 May 2023 08:51:36 -0700 Subject: PR #1433: Fix incorrect timespec definition on 32-bit platforms with 64-bit time_t Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1433 Some 32-bit configurations may use 64-bit time_t, which leads to different layout of userspace timespec and the one expected by SYS_futex implementation in kernel. In particular the issue occurs when using musl libc which has switched to unconditional 64-bit time_t definition. This patch introduces custom struct timespec with two longs when old SYS_futex is used to match the kernel timespec definition. Merge 2eaca415da825b3f31a90f58a35bdef2b6d2a6c5 into f8bf909108b3604a00590a074f2986c0895514d2 Merging this change closes #1433 COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1433 from olegartys:futex_time64_bug 2eaca415da825b3f31a90f58a35bdef2b6d2a6c5 PiperOrigin-RevId: 528796119 Change-Id: Idaa952f64bd97c6dc9703a8b44deac43e29ff9ae --- absl/synchronization/internal/futex.h | 48 ++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 6 deletions(-) (limited to 'absl/synchronization/internal') diff --git a/absl/synchronization/internal/futex.h b/absl/synchronization/internal/futex.h index 55078f14..573c01b7 100644 --- a/absl/synchronization/internal/futex.h +++ b/absl/synchronization/internal/futex.h @@ -32,6 +32,7 @@ #include #include +#include #include "absl/base/optimization.h" #include "absl/synchronization/internal/kernel_timeout.h" @@ -79,6 +80,18 @@ namespace synchronization_internal { #if defined(SYS_futex_time64) && !defined(SYS_futex) #define SYS_futex SYS_futex_time64 +using FutexTimespec = struct timespec; +#else +// Some libc implementations have switched to an unconditional 64-bit `time_t` +// definition. This means that `struct timespec` may not match the layout +// expected by the kernel ABI on 32-bit platforms. So we define the +// FutexTimespec that matches the kernel timespec definition. It should be safe +// to use this struct for 64-bit userspace builds too, since it will use another +// SYS_futex kernel call with 64-bit tv_sec inside timespec. +struct FutexTimespec { + long tv_sec; // NOLINT + long tv_nsec; // NOLINT +}; #endif class FutexImpl { @@ -93,12 +106,13 @@ class FutexImpl { // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`. static int WaitAbsoluteTimeout(std::atomic* v, int32_t val, const struct timespec* abs_timeout) { + FutexTimespec ts; // https://locklessinc.com/articles/futex_cheat_sheet/ // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. - auto err = - syscall(SYS_futex, reinterpret_cast(v), - FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, - val, abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); + auto err = syscall( + SYS_futex, reinterpret_cast(v), + FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val, + ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY); if (err != 0) { return -errno; } @@ -109,10 +123,12 @@ class FutexImpl { // `*rel_timeout` has elapsed, or until woken by `Wake()`. static int WaitRelativeTimeout(std::atomic* v, int32_t val, const struct timespec* rel_timeout) { + FutexTimespec ts; // Atomically check that the futex value is still 0, and if it // is, sleep until abs_timeout or until woken by FUTEX_WAKE. - auto err = syscall(SYS_futex, reinterpret_cast(v), - FUTEX_PRIVATE_FLAG, val, rel_timeout); + auto err = + syscall(SYS_futex, reinterpret_cast(v), FUTEX_PRIVATE_FLAG, + val, ToFutexTimespec(rel_timeout, &ts)); if (err != 0) { return -errno; } @@ -128,6 +144,26 @@ class FutexImpl { } return 0; } + + private: + static FutexTimespec* ToFutexTimespec(const struct timespec* userspace_ts, + FutexTimespec* futex_ts) { + if (userspace_ts == nullptr) { + return nullptr; + } + + using FutexSeconds = decltype(futex_ts->tv_sec); + using FutexNanoseconds = decltype(futex_ts->tv_nsec); + + constexpr auto kMaxSeconds{(std::numeric_limits::max)()}; + if (userspace_ts->tv_sec > kMaxSeconds) { + futex_ts->tv_sec = kMaxSeconds; + } else { + futex_ts->tv_sec = static_cast(userspace_ts->tv_sec); + } + futex_ts->tv_nsec = static_cast(userspace_ts->tv_nsec); + return futex_ts; + } }; class Futex : public FutexImpl {}; -- cgit v1.2.3