mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-11 11:56:16 +01:00
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
This commit is contained in:
parent
606bdcdb18
commit
eec65c761b
@ -1599,6 +1599,13 @@ g_cond_wait_until (GCond *cond,
|
|||||||
{
|
{
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
struct timespec span;
|
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;
|
guint sampled;
|
||||||
int res;
|
int res;
|
||||||
gboolean success;
|
gboolean success;
|
||||||
@ -1618,9 +1625,33 @@ g_cond_wait_until (GCond *cond,
|
|||||||
if (span.tv_sec < 0)
|
if (span.tv_sec < 0)
|
||||||
return FALSE;
|
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];
|
sampled = cond->i[0];
|
||||||
g_mutex_unlock (mutex);
|
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;
|
success = (res < 0 && errno == ETIMEDOUT) ? FALSE : TRUE;
|
||||||
g_mutex_lock (mutex);
|
g_mutex_lock (mutex);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user