Skip to content

Commit 1e931cb

Browse files
authored
Merge pull request #527 from totoroyyb/master
fix: broken futex syscall when FUTEX_WAIT is used
2 parents ebe76e2 + 026a262 commit 1e931cb

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

src/Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,24 @@
4949
#
5050
# INTERCEPT_SYSCALL
5151
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
52-
# If enabled, this currently only works to divert the getrandom syscall.
52+
# If enabled, this currently only works a few types of syscalls,
53+
# including FUTEX (see below), clock_gettime, etc.
5354
#
5455
# - note that on unusual architectures, if INTERCEPT_SYSCALL is set, you may
5556
# need to explicitly define variadic_promotion_t (e.g. by putting
5657
# -Dvariadic_promotion_t=int into CFLAGS). See src/faketime_common.h for
5758
# more info.
5859
#
60+
# INTERCEPT_FUTEX
61+
# - (On GNU/Linux only) intercept glibc's syscall() for relevant FUTEX syscalls.
62+
# If enabled, FUTEX syscalls will be intercepted and translated correspondingly.
63+
# - when FUTEX_WAIT_BITSET (with absolute deadline) is set, the deadline will
64+
# be adjusted based on the faketime.
65+
# - when FUTEX_WAKE (with relative deadline) is set, the deadline will be
66+
# adjusted based on the time rate alone.
67+
# - for other FUTEX operations, no adjustment is made for now.
68+
#
69+
#
5970
# FAKE_STATELESS
6071
# - Remove support for any functionality that requires sharing state across
6172
# threads of a process, or different processes. This decreases the risk of

src/libfaketime.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4439,18 +4439,34 @@ pid_t getpid() {
44394439

44404440
#ifdef INTERCEPT_SYSCALL
44414441
#ifdef INTERCEPT_FUTEX
4442-
long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t val, struct timespec* timeout, uint32_t* uaddr2, uint32_t val3) {
4442+
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) {
44434443
if (timeout == NULL) {
44444444
// not timeout related, just call the real syscall
4445+
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
4446+
}
4447+
if (timeout->tv_sec < 0) {
4448+
// fprintf(stderr, "libfaketime: invalid timeout.tv_sec < 0\n");
4449+
timeout->tv_sec = 0;
4450+
}
4451+
if (timeout->tv_nsec < 0) {
4452+
// fprintf(stderr, "libfaketime: invalid timeout.tv_nsec < 0\n");
4453+
timeout->tv_nsec = 0;
4454+
}
4455+
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
4456+
}
4457+
4458+
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) {
4459+
if (timeout == NULL) {
44454460
goto futex_fallback;
44464461
}
44474462

4448-
// if ((futex_op & FUTEX_CMD_MASK) == FUTEX_WAIT_BITSET) {
4449-
if (1) {
4450-
clockid_t clk_id = CLOCK_MONOTONIC;
4451-
if (futex_op & FUTEX_CLOCK_REALTIME)
4452-
clk_id = CLOCK_REALTIME;
4463+
int futex_cmd = futex_op & FUTEX_CMD_MASK;
4464+
clockid_t clk_id = CLOCK_MONOTONIC;
4465+
if (futex_op & FUTEX_CLOCK_REALTIME)
4466+
clk_id = CLOCK_REALTIME;
44534467

4468+
if (futex_cmd == FUTEX_WAIT_BITSET) {
4469+
// FUTEX_WAIT_BITSET uses absolute timeout
44544470
struct timespec real_tp, fake_tp;
44554471

44564472
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
44634479
struct timespec adjusted_timeout, time_diff;
44644480
timespecsub(&fake_tp, &real_tp, &time_diff);
44654481
timespecsub(timeout, &time_diff, &adjusted_timeout);
4466-
// fprintf(stdout, "libfaketime: adjusted timeout: %ld.%09ld\n", adjusted_timeout.tv_sec, adjusted_timeout.tv_nsec);
4467-
long result;
4468-
result = real_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
4482+
long result = make_futex_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
44694483
if (result != 0) {
44704484
return result;
44714485
}
@@ -4498,11 +4512,8 @@ long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t v
44984512
struct timespec real_timeout;
44994513
timespecadd(&real_now, &wait_time, &real_timeout);
45004514

4501-
// fprintf(stdout, "libfaketime: recalculated real timeout: %ld.%09ld\n",
4502-
// real_timeout.tv_sec, real_timeout.tv_nsec);
4503-
45044515
// Call the real syscall with the recalculated timeout
4505-
result = real_syscall(number, uaddr, futex_op, val, &real_timeout, uaddr2, val3);
4516+
result = make_futex_syscall(number, uaddr, futex_op, val, &real_timeout, uaddr2, val3);
45064517
if (result != 0) {
45074518
return result;
45084519
}
@@ -4513,12 +4524,24 @@ long handle_futex_syscall(long number, uint32_t* uaddr, int futex_op, uint32_t v
45134524
}
45144525
}
45154526
return 0;
4527+
} else if (futex_cmd == FUTEX_WAIT) {
4528+
// FUTEX_WAIT uses relative timeout - scale by time rate
4529+
struct timespec adjusted_timeout;
4530+
4531+
if (user_rate_set && !dont_fake && ((clk_id == CLOCK_REALTIME) || (clk_id == CLOCK_MONOTONIC))) {
4532+
timespecmul(timeout, 1.0 / user_rate, &adjusted_timeout);
4533+
} else {
4534+
adjusted_timeout = *timeout;
4535+
}
4536+
4537+
return make_futex_syscall(number, uaddr, futex_op, val, &adjusted_timeout, uaddr2, val3);
45164538
} else {
4517-
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
4539+
// Other futex operations - pass through unchanged
4540+
goto futex_fallback;
45184541
}
45194542

45204543
futex_fallback:
4521-
return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
4544+
return make_futex_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3);
45224545
}
45234546
#endif
45244547

0 commit comments

Comments
 (0)