Commit c0d58db0 by Oleg Lyovin Committed by Copybara-Service

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 f8bf9091

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
parent 502769bf
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <atomic> #include <atomic>
#include <cstdint> #include <cstdint>
#include <limits>
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/synchronization/internal/kernel_timeout.h" #include "absl/synchronization/internal/kernel_timeout.h"
...@@ -79,6 +80,18 @@ namespace synchronization_internal { ...@@ -79,6 +80,18 @@ namespace synchronization_internal {
#if defined(SYS_futex_time64) && !defined(SYS_futex) #if defined(SYS_futex_time64) && !defined(SYS_futex)
#define SYS_futex SYS_futex_time64 #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 #endif
class FutexImpl { class FutexImpl {
...@@ -93,12 +106,13 @@ class FutexImpl { ...@@ -93,12 +106,13 @@ class FutexImpl {
// CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`. // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
const struct timespec* abs_timeout) { const struct timespec* abs_timeout) {
FutexTimespec ts;
// https://locklessinc.com/articles/futex_cheat_sheet/ // https://locklessinc.com/articles/futex_cheat_sheet/
// Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time. // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
auto err = auto err = syscall(
syscall(SYS_futex, reinterpret_cast<int32_t*>(v), SYS_futex, reinterpret_cast<int32_t*>(v),
FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME, val,
val, abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY); ToFutexTimespec(abs_timeout, &ts), nullptr, FUTEX_BITSET_MATCH_ANY);
if (err != 0) { if (err != 0) {
return -errno; return -errno;
} }
...@@ -109,10 +123,12 @@ class FutexImpl { ...@@ -109,10 +123,12 @@ class FutexImpl {
// `*rel_timeout` has elapsed, or until woken by `Wake()`. // `*rel_timeout` has elapsed, or until woken by `Wake()`.
static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val, static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
const struct timespec* rel_timeout) { const struct timespec* rel_timeout) {
FutexTimespec ts;
// Atomically check that the futex value is still 0, and if it // Atomically check that the futex value is still 0, and if it
// is, sleep until abs_timeout or until woken by FUTEX_WAKE. // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v), auto err =
FUTEX_PRIVATE_FLAG, val, rel_timeout); syscall(SYS_futex, reinterpret_cast<int32_t*>(v), FUTEX_PRIVATE_FLAG,
val, ToFutexTimespec(rel_timeout, &ts));
if (err != 0) { if (err != 0) {
return -errno; return -errno;
} }
...@@ -128,6 +144,26 @@ class FutexImpl { ...@@ -128,6 +144,26 @@ class FutexImpl {
} }
return 0; 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<FutexSeconds>::max)()};
if (userspace_ts->tv_sec > kMaxSeconds) {
futex_ts->tv_sec = kMaxSeconds;
} else {
futex_ts->tv_sec = static_cast<FutexSeconds>(userspace_ts->tv_sec);
}
futex_ts->tv_nsec = static_cast<FutexNanoseconds>(userspace_ts->tv_nsec);
return futex_ts;
}
}; };
class Futex : public FutexImpl {}; class Futex : public FutexImpl {};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment