Merge pull request #527 from totoroyyb/master

fix: broken futex syscall when FUTEX_WAIT is used
This commit is contained in:
Wolfgang Hommel
2025-12-23 11:22:48 +01:00
committed by GitHub
2 changed files with 50 additions and 16 deletions

View File

@@ -49,13 +49,24 @@
#
# INTERCEPT_SYSCALL
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
# If enabled, this currently only works to divert the getrandom syscall.
# If enabled, this currently only works a few types of syscalls,
# including FUTEX (see below), clock_gettime, etc.
#
# - note that on unusual architectures, if INTERCEPT_SYSCALL is set, you may
# need to explicitly define variadic_promotion_t (e.g. by putting
# -Dvariadic_promotion_t=int into CFLAGS). See src/faketime_common.h for
# more info.
#
# INTERCEPT_FUTEX
# - (On GNU/Linux only) intercept glibc's syscall() for relevant FUTEX syscalls.
# If enabled, FUTEX syscalls will be intercepted and translated correspondingly.
# - when FUTEX_WAIT_BITSET (with absolute deadline) is set, the deadline will
# be adjusted based on the faketime.
# - when FUTEX_WAKE (with relative deadline) is set, the deadline will be
# adjusted based on the time rate alone.
# - for other FUTEX operations, no adjustment is made for now.
#
#
# FAKE_STATELESS
# - Remove support for any functionality that requires sharing state across
# threads of a process, or different processes. This decreases the risk of

View File

@@ -4439,18 +4439,34 @@ pid_t getpid() {
#ifdef INTERCEPT_SYSCALL
#ifdef INTERCEPT_FUTEX
long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t val, struct timespec* timeout, uint32_t* uaddr2, uint32_t val3) {
static inline long make_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t val, struct timespec* timeout, uint32_t* uaddr2, uint32_t val3) {
if (timeout == NULL) {
// not timeout related, just call the real syscall
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
}
if (timeout->tv_sec < 0) {
// fprintf(stderr, "libfaketime: invalid timeout.tv_sec < 0\n");
timeout->tv_sec = 0;
}
if (timeout->tv_nsec < 0) {
// fprintf(stderr, "libfaketime: invalid timeout.tv_nsec < 0\n");
timeout->tv_nsec = 0;
}
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
}
static inline long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t val, struct timespec* timeout, uint32_t* uaddr2, uint32_t val3) {
if (timeout == NULL) {
goto futex_fallback;
}
// if ((futex_op & FUTEX_CMD_MASK) == FUTEX_WAIT_BITSET) {
if (1) {
clockid_t clk_id = CLOCK_MONOTONIC;
if (futex_op & FUTEX_CLOCK_REALTIME)
clk_id = CLOCK_REALTIME;
int futex_cmd = futex_op & FUTEX_CMD_MASK;
clockid_t clk_id = CLOCK_MONOTONIC;
if (futex_op & FUTEX_CLOCK_REALTIME)
clk_id = CLOCK_REALTIME;
if (futex_cmd == FUTEX_WAIT_BITSET) {
// FUTEX_WAIT_BITSET uses absolute timeout
struct timespec real_tp, fake_tp;
DONT_FAKE_TIME((*real_clock_gettime)(clk_id, &real_tp));
@@ -4463,9 +4479,7 @@ long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t v
struct timespec adjusted_timeout, time_diff;
timespecsub(&fake_tp, &real_tp, &time_diff);
timespecsub(timeout, &time_diff, &adjusted_timeout);
// fprintf(stdout, "libfaketime: adjusted timeout: %ld.%09ld\n", adjusted_timeout.tv_sec, adjusted_timeout.tv_nsec);
long result;
result = real_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
long result = make_futex_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
if (result != 0) {
return result;
}
@@ -4498,11 +4512,8 @@ long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t v
struct timespec real_timeout;
timespecadd(&real_now, &wait_time, &real_timeout);
// fprintf(stdout, "libfaketime: recalculated real timeout: %ld.%09ld\n",
// real_timeout.tv_sec, real_timeout.tv_nsec);
// Call the real syscall with the recalculated timeout
result = real_syscall(number, uaddr, futex_op, val, &real_timeout, uaddr2, val3);
result = make_futex_syscall(number, uaddr, futex_op, val, &real_timeout, uaddr2, val3);
if (result != 0) {
return result;
}
@@ -4513,12 +4524,24 @@ long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t v
}
}
return 0;
} else if (futex_cmd == FUTEX_WAIT) {
// FUTEX_WAIT uses relative timeout - scale by time rate
struct timespec adjusted_timeout;
if (user_rate_set && !dont_fake && ((clk_id == CLOCK_REALTIME) || (clk_id == CLOCK_MONOTONIC))) {
timespecmul(timeout, 1.0 / user_rate, &adjusted_timeout);
} else {
adjusted_timeout = *timeout;
}
return make_futex_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
} else {
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
// Other futex operations - pass through unchanged
goto futex_fallback;
}
futex_fallback:
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
return make_futex_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
}
#endif