[glib: 1/2] gthread: Fix futex timespec type on 32-bit kernels with 64-bit userspace




commit eec65c761bb406beccf674e371ea38b231136707
Author: Philip Withnall <pwithnall endlessos org>
Date:   Tue Apr 26 11:50:23 2022 +0100

    gthread: Fix futex timespec type on 32-bit kernels with 64-bit userspace
    
    The `struct timespec` type documented as being passed to the `futex()`
    syscall actually needs to be the *kernel’s* timespec type. This will be
    a different width from the userspace timespec type if running a 64-bit
    userspace on a 32-bit kernel.
    
    That mismatch will cause `g_cond_wait_until()` to return `FALSE`
    immediately.
    
    No other uses of `futex()` in GLib use the timeout argument, so they’re
    all OK.
    
    Following a detailed suggestion by Rich Felker, pass a different
    timespec type into `futex()` if `__NR_futex_time64` is defined. That’s
    the 64-bit time version of `futex()` which was added in kernel 5.1, and
    which was only added for 32-bit kernels.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>
    
    Fixes: #2634

 glib/gthread-posix.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)
---
diff --git a/glib/gthread-posix.c b/glib/gthread-posix.c
index 8e2e66db54..8c403f975a 100644
--- a/glib/gthread-posix.c
+++ b/glib/gthread-posix.c
@@ -1599,6 +1599,13 @@ g_cond_wait_until (GCond  *cond,
 {
   struct timespec now;
   struct timespec span;
+#ifdef __NR_futex_time64
+  long span_arg[2];
+  G_STATIC_ASSERT (sizeof (span_arg[0]) == 4);
+#else
+  struct timespec span_arg;
+#endif
+
   guint sampled;
   int res;
   gboolean success;
@@ -1618,9 +1625,33 @@ g_cond_wait_until (GCond  *cond,
   if (span.tv_sec < 0)
     return FALSE;
 
+  /* On x32 (ILP32 ABI on x86_64) and potentially sparc64, the raw futex()
+   * syscall takes a 32-bit timespan argument *regardless* of whether userspace
+   * is using 32-bit or 64-bit `struct timespec`. This means that we can’t
+   * unconditionally pass a `struct timespec` pointer into the syscall.
+   *
+   * Assume that any such platform is new enough to define the
+   * `__NR_futex_time64` workaround syscall (which accepts 64-bit timespecs,
+   * introduced in kernel 5.1), and use that as a proxy for whether to pass in
+   * `long[2]` or `struct timespec`.
+   *
+   * As per https://lwn.net/Articles/776427/, the `time64` syscalls only exist
+   * on 32-bit platforms, so in this case `sizeof(long)` should always be
+   * 32 bits.
+   *
+   * Don’t bother actually calling `__NR_futex_time64` as the `span` is relative
+   * and hence very unlikely to overflow, even if using 32-bit longs.
+   */
+#ifdef __NR_futex_time64
+  span_arg[0] = span.tv_sec;
+  span_arg[1] = span.tv_nsec;
+#else
+  span_arg = span;
+#endif
+
   sampled = cond->i[0];
   g_mutex_unlock (mutex);
-  res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span);
+  res = syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, &span_arg);
   success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
   g_mutex_lock (mutex);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]