Package: faketime X-Debbugs-Cc: [email protected] Version: 0.9.10+2024-06-05+gba9ed5b2-0.6 Severity: normal
I have prepared an NMU for faketime. This upload updates the package to upstream 0.9.12, drops obsolete Debian patches that are now included upstream, and includes two upstream cherry-picks for the utimensat(2)/futimens(2) fix and its regression test. The proposed version is 0.9.12-0.1, and a debdiff is attached. This NMU is being done under the maintainer's LowThresholdNmu preference. I verified that the source package builds successfully, a clean sbuild on unstable/amd64 succeeds, lintian passes with no issues beyond info tags, and the smoketest and upstream autopkgtests pass. The simple-cprogs autopkgtest failed once locally, but it appears to be a pre-existing 1-second wrapper timing flake, and a CI rerun passed. I am sending this notice and proceeding with the upload without delay.
diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/changelog faketime-0.9.12/debian/changelog --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/changelog 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/changelog 2026-03-28 13:53:05.000000000 -0400 @@ -1,3 +1,14 @@ +faketime (0.9.12-0.1) unstable; urgency=medium + + * Non-maintainer upload. + * New upstream version 0.9.12 + * Add salsa-ci.yml + * d/patches: + - Remove outdated patches. + - Add upstream fix for utimens now handling + + -- Aquila Macedo Costa <[email protected]> Sat, 28 Mar 2026 13:53:05 -0400 + faketime (0.9.10+2024-06-05+gba9ed5b2-0.6) unstable; urgency=medium * Non-maintainer upload. diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0002-Fix-interposition-of-clock_gettime64.patch faketime-0.9.12/debian/patches/0002-Fix-interposition-of-clock_gettime64.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0002-Fix-interposition-of-clock_gettime64.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0002-Fix-interposition-of-clock_gettime64.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,57 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Fri, 17 Jan 2025 12:05:09 +0000 -Subject: Fix interposition of clock_gettime64 - -timespec.tv_nsec is 32-bit, even though timeval.tv_usec is -64-bit (weirdly). This doesn't matter very much in practice because - * on little endian architectures (which is all our 32-bit release - arches) writing to a too big integer ends up writing the - desired value in the desired location, and - * it doesn't affect the overall struct size on any of our actual - architectures (which align the uint64_t to 8 so must make the - whole struct 16 not 12), so the write overflow is harmless. - -> #include <time.h> -> #include <sys/time.h> -> #include <stdio.h> -> struct timeval tv; -> struct timespec ts; -> int main(void) { -> printf("time_t %lld\n", (unsigned long long) sizeof(time_t)); -> printf("timeval %lld %lld %lld\n", -> (unsigned long long) sizeof(tv), -> (unsigned long long) sizeof(tv.tv_sec), -> (unsigned long long) sizeof(tv.tv_usec) -> ); -> printf("timespec %lld %lld %lld\n", -> (unsigned long long) sizeof(ts), -> (unsigned long long) sizeof(ts.tv_sec), -> (unsigned long long) sizeof(ts.tv_nsec) -> ); -> } -> (sid_armhf-dchroot)iwj@amdahl:~/Faketime/test$ gcc t.c -> (sid_armhf-dchroot)iwj@amdahl:~/Faketime/test$ ./a.out -> time_t 8 -> timeval 16 8 8 -> timespec 16 8 4 -> (sid_armhf-dchroot)iwj@amdahl:~/Faketime/test$ - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/487 ---- - src/libfaketime.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index cb37bcf..d93bfa1 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -168,7 +168,7 @@ struct utimbuf { - struct __timespec64 - { - uint64_t tv_sec; /* Seconds */ -- uint64_t tv_nsec; /* Nanoseconds */ -+ uint32_t tv_nsec; /* this is 32-bit, apparently! */ - }; - - /* diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0003-Interpose-__time64.patch faketime-0.9.12/debian/patches/0003-Interpose-__time64.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0003-Interpose-__time64.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0003-Interpose-__time64.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,42 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Fri, 17 Jan 2025 09:03:21 +0000 -Subject: Interpose __time64 - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/487 ---- - src/libfaketime.c | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index d93bfa1..aa78c33 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -2437,6 +2437,27 @@ int __clock_gettime64(clockid_t clk_id, struct __timespec64 *tp64) - return result; - } - -+/* this is used by 32-bit architectures only */ -+uint64_t __time64(uint64_t *write_out) -+{ -+ struct timespec tp; -+ uint64_t output; -+ int error; -+ -+ error = clock_gettime(CLOCK_REALTIME, &tp); -+ if (error == -1) -+ { -+ return (uint64_t)error; -+ } -+ output = tp.tv_sec; -+ -+ if (write_out) -+ { -+ *write_out = output; -+ } -+ return output; -+} -+ - #ifdef TIME_UTC - #ifdef MACOS_DYLD_INTERPOSE - int macos_timespec_get(struct timespec *ts, int base) diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0004-Interpose-gettimeofday64.patch faketime-0.9.12/debian/patches/0004-Interpose-gettimeofday64.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0004-Interpose-gettimeofday64.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0004-Interpose-gettimeofday64.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,47 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Fri, 17 Jan 2025 12:08:23 +0000 -Subject: Interpose gettimeofday64 - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/487 ---- - src/libfaketime.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index aa78c33..2123d83 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -171,6 +171,13 @@ struct __timespec64 - uint32_t tv_nsec; /* this is 32-bit, apparently! */ - }; - -+/* __timespec64 is needed for clock_gettime64 on 32-bit architectures */ -+struct __timeval64 -+{ -+ uint64_t tv_sec; /* Seconds */ -+ uint64_t tv_usec; /* this is 64-bit, apparently! */ -+}; -+ - /* - * Per thread variable, which we turn on inside real_* calls to avoid modifying - * time multiple times of for the whole process to prevent faking time -@@ -2437,6 +2444,18 @@ int __clock_gettime64(clockid_t clk_id, struct __timespec64 *tp64) - return result; - } - -+/* this is used by 32-bit architectures only */ -+int __gettimeofday64(struct __timeval64 *tv64, void *tz) -+{ -+ struct timeval tv; -+ int result; -+ -+ result = gettimeofday(&tv, tz); -+ tv64->tv_sec = tv.tv_sec; -+ tv64->tv_usec = tv.tv_usec; -+ return result; -+} -+ - /* this is used by 32-bit architectures only */ - uint64_t __time64(uint64_t *write_out) - { diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0005-Re-disable-faking-utime-by-default.patch faketime-0.9.12/debian/patches/0005-Re-disable-faking-utime-by-default.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0005-Re-disable-faking-utime-by-default.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0005-Re-disable-faking-utime-by-default.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,30 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Sun, 19 Jan 2025 22:17:08 +0000 -Subject: Re-disable faking utime by default - -Fixes - https://github.com/wolfcw/libfaketime/issues/483 - -See also - https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1093412#35 -Hopefully this will fix Debian #1093412. - -This patch is now upstream MR - https://github.com/wolfcw/libfaketime/pull/486 ---- - src/libfaketime.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index 2123d83..c0e793a 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -891,7 +891,7 @@ static bool load_time(struct timespec *tp) - * ======================================================================= - */ - #ifdef FAKE_UTIME --static int fake_utime_disabled = 0; -+static int fake_utime_disabled = 1; - #endif - - diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0006-Replace-data-race-with-use-of-pthread_once-ftpl_init.patch faketime-0.9.12/debian/patches/0006-Replace-data-race-with-use-of-pthread_once-ftpl_init.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0006-Replace-data-race-with-use-of-pthread_once-ftpl_init.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0006-Replace-data-race-with-use-of-pthread_once-ftpl_init.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,70 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Tue, 21 Jan 2025 18:44:42 +0000 -Subject: Replace data race with use of pthread_once (ftpl_init) - -At the cost of no longer nicely detecting recursive initialisation -problems. - -Closes: #1093599 - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/488 ---- - src/libfaketime.c | 16 ++++++++++------ - 1 file changed, 10 insertions(+), 6 deletions(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index c0e793a..0cbdbf9 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -319,7 +319,7 @@ static bool check_missing_real(const char *name, bool missing) - #define CHECK_MISSING_REAL(name) \ - check_missing_real(#name, (NULL == real_##name)) - --static int initialized = 0; -+static pthread_once_t initialized_once_control = PTHREAD_ONCE_INIT; - - /* prototypes */ - static int fake_gettimeofday(struct timeval *tv); -@@ -2361,6 +2361,13 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) - #endif - { - int result; -+ ftpl_init(); -+ // If ftpl_init ends up recursing, pthread_once will deadlock. -+ // So the remaining recursion code is now unreachable. -+ // Hopefully this doesn't happen in practice. -+ -+/* -+ - static int recursion_depth = 0; - - if (!initialized) -@@ -2399,6 +2406,7 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) - #endif - recursion_depth--; - } -+ */ - /* sanity check */ - if (tp == NULL) - { -@@ -2859,7 +2867,6 @@ static void ftpl_really_init(void) - - #undef dlsym - #undef dlvsym -- initialized = 1; - - #ifdef FAKE_STATELESS - if (0) ft_shm_init(); -@@ -3110,10 +3117,7 @@ static void ftpl_really_init(void) - } - - inline static void ftpl_init(void) { -- if (!initialized) -- { -- ftpl_really_init(); -- } -+ pthread_once(&initialized_once_control, ftpl_really_init); - } - - void *ft_dlvsym(void *handle, const char *symbol, const char *version, diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0007-Replace-data-race-with-use-of-pthread_once-ft_shm_in.patch faketime-0.9.12/debian/patches/0007-Replace-data-race-with-use-of-pthread_once-ft_shm_in.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0007-Replace-data-race-with-use-of-pthread_once-ft_shm_in.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0007-Replace-data-race-with-use-of-pthread_once-ft_shm_in.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,48 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Tue, 21 Jan 2025 18:50:07 +0000 -Subject: Replace data race with use of pthread_once (ft_shm_init) - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/488 ---- - src/libfaketime.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index 0cbdbf9..611f4db 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -536,16 +536,20 @@ static void ft_shm_destroy(void) - } - } - -+static pthread_once_t ft_shm_initialized_once_control = PTHREAD_ONCE_INIT; -+ -+static void ft_shm_really_init (void); - static void ft_shm_init (void) -+{ -+ pthread_once(&ft_shm_initialized_once_control, ft_shm_really_init); -+} -+ -+static void ft_shm_really_init (void) - { - int ticks_shm_fd; - char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); - sem_t *shared_semR = NULL; - static int nt=1; -- static int ft_shm_initialized = 0; -- -- /* do all of this once only */ -- if (ft_shm_initialized > 0) return; - - /* create semaphore and shared memory locally unless it has been passed along */ - if (ft_shared_env == NULL) -@@ -625,8 +629,6 @@ static void ft_shm_init (void) - { /* force the deletion of the shm sync env variable */ - unsetenv("FAKETIME_SHARED"); - } -- -- ft_shm_initialized = 1; - } - - static void ft_cleanup (void) diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0008-Call-ftpl_init-before-using-monotonic_conds_lock.patch faketime-0.9.12/debian/patches/0008-Call-ftpl_init-before-using-monotonic_conds_lock.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0008-Call-ftpl_init-before-using-monotonic_conds_lock.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0008-Call-ftpl_init-before-using-monotonic_conds_lock.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,39 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Wed, 22 Jan 2025 09:11:46 +0000 -Subject: Call ftpl_init before using monotonic_conds_lock - -Otherwise we can use this in an uninitialised state, which is not -allowed. - -We call ftpl_init in pthread_cond_init_232, but the application might -not have called that. For example, it might have a static condition -variable set up with PTHREAD_COND_INITIALIZER. - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/488 ---- - src/libfaketime.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index 611f4db..cc3a5b5 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -3809,6 +3809,8 @@ int pthread_cond_destroy_232(pthread_cond_t *cond) - { - struct pthread_cond_monotonic* e; - -+ ftpl_init(); -+ - if (pthread_rwlock_trywrlock(&monotonic_conds_lock) != 0) { - sched_yield(); - return EBUSY; -@@ -3891,6 +3893,8 @@ int pthread_cond_timedwait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, - clockid_t clk_id; - int result = 0; - -+ ftpl_init(); -+ - if (abstime != NULL) - { - if (pthread_rwlock_tryrdlock(&monotonic_conds_lock) != 0) { diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0009-Don-t-use-_try_-locking-calls-for-monotonic_conds_lo.patch faketime-0.9.12/debian/patches/0009-Don-t-use-_try_-locking-calls-for-monotonic_conds_lo.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0009-Don-t-use-_try_-locking-calls-for-monotonic_conds_lo.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0009-Don-t-use-_try_-locking-calls-for-monotonic_conds_lo.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,82 +0,0 @@ -From: Ian Jackson <[email protected]> -Date: Wed, 22 Jan 2025 09:29:58 +0000 -Subject: Don't use _try_ locking calls for monotonic_conds_lock - -This reverts commit 8ef74e33b636a53a757a945d8ebc51d0986f0d81 - "Swapped out pthread_rwlock_xxlock() ..." - -This could result in concurrent uses of pthread_cond_* erroneously -returning EAGAIN, which is not permitted by the spec and which the -application way well treat as a bug. This seems to be happening in -gem2deb in ci.Debian.net. - -The commit message in 8ef74e33b636 says (rewrapped) - - Swapped out pthread_rwlock_xxlock(), which doesn't return if it - can't obtain the lock, with pthread_rwlock_xxtrylock() followed by - sched yield and error code return. The issue is sometimes a thread - calling pthread_cond_init() or pthread_cond_destroy() can't - acquire the lock when another thread is waiting on a condition - variable notification via pthread_cond_timedwait(), and thus the - thread calling pthread_cond_init() or pthread_cond_destroy() end - up hanging indefinitely. - -I don't think this is true. The things that are done with -monotonic_conds_lock held are HASH_ADD_PTR HASH_FIND_PTR etc. on -monotonic_conds, which should all be fast and AFAICT don't in turn -take any locks. So it shouldn't deadlock. - -I conjecture that the underlying bug being experienced by the author -of "Swapped out pthread_rwlock_xxlock" was the lack of ftpl_init - ie, -access to an uninitialised pthread_rwlock_t. That might result in a -hang. - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/488 ---- - src/libfaketime.c | 18 +++++++++--------- - 1 file changed, 9 insertions(+), 9 deletions(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index cc3a5b5..e03dec7 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -3794,9 +3794,9 @@ int pthread_cond_init_232(pthread_cond_t *restrict cond, const pthread_condattr_ - struct pthread_cond_monotonic *e = (struct pthread_cond_monotonic*)malloc(sizeof(struct pthread_cond_monotonic)); - e->ptr = cond; - -- if (pthread_rwlock_trywrlock(&monotonic_conds_lock) != 0) { -- sched_yield(); -- return EAGAIN; -+ if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) { -+ fprintf(stderr,"can't acquire write monotonic_conds_lock\n"); -+ exit(-1); - } - HASH_ADD_PTR(monotonic_conds, ptr, e); - pthread_rwlock_unlock(&monotonic_conds_lock); -@@ -3811,9 +3811,9 @@ int pthread_cond_destroy_232(pthread_cond_t *cond) - - ftpl_init(); - -- if (pthread_rwlock_trywrlock(&monotonic_conds_lock) != 0) { -- sched_yield(); -- return EBUSY; -+ if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) { -+ fprintf(stderr,"can't acquire write monotonic_conds_lock\n"); -+ exit(-1); - } - HASH_FIND_PTR(monotonic_conds, &cond, e); - if (e) { -@@ -3897,9 +3897,9 @@ int pthread_cond_timedwait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, - - if (abstime != NULL) - { -- if (pthread_rwlock_tryrdlock(&monotonic_conds_lock) != 0) { -- sched_yield(); -- return EAGAIN; -+ if (pthread_rwlock_rdlock(&monotonic_conds_lock) != 0) { -+ fprintf(stderr,"can't acquire read monotonic_conds_lock\n"); -+ exit(-1); - } - HASH_FIND_PTR(monotonic_conds, &cond, e); - pthread_rwlock_unlock(&monotonic_conds_lock); diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0010-485.patch faketime-0.9.12/debian/patches/0010-485.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/0010-485.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/0010-485.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,36 +0,0 @@ -From: Gianfranco Costamagna <[email protected]> -Date: Wed, 22 Jan 2025 12:22:37 +0100 -Subject: [PATCH] test/libmallocintercept.c: fix write function unused return - value -MIME-Version: 1.0 -Content-Type: text/plain; charset="utf-8" -Content-Transfer-Encoding: 8bit - -We should ignore the return value for logging function, to fix a new gcc ftbfs -libmallocintercept.c: In function ‘print_msg’: -libmallocintercept.c:27:9: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result] - 27 | write(0, msg, strlen(msg)); - | ^~~~~~~~~~~~~~~~~~~~~~~~~~ - - -This was merged upstream in this MR: - https://github.com/wolfcw/libfaketime/pull/485 ---- - test/libmallocintercept.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/test/libmallocintercept.c b/test/libmallocintercept.c -index e789d34..61edbb8 100644 ---- a/test/libmallocintercept.c -+++ b/test/libmallocintercept.c -@@ -24,7 +24,9 @@ - #include <unistd.h> - - static void print_msg(const char *msg) { -- write(0, msg, strlen(msg)); -+ size_t out; -+ out = write(0, msg, strlen(msg)); -+ (void) out; /* unused */ - } - - static void* actual_malloc(size_t size) { diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/add-regression-test-for-utimens-now.patch faketime-0.9.12/debian/patches/add-regression-test-for-utimens-now.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/add-regression-test-for-utimens-now.patch 1969-12-31 19:00:00.000000000 -0500 +++ faketime-0.9.12/debian/patches/add-regression-test-for-utimens-now.patch 2026-03-28 13:53:05.000000000 -0400 @@ -0,0 +1,101 @@ +From: Aquila Macedo <[email protected]> +Date: Fri, 27 Mar 2026 16:09:02 -0300 +Subject: Add regression test for utimensat/futimens now handling +Origin: upstream, 483a7a703cd614452e096aa6a483cbf834c8b069 + +Exercise the NULL and UTIME_NOW timestamp paths in timetest so the +file-timestamp "set to now" case is covered by the test suite. +--- + test/timetest.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 65 insertions(+) + +diff --git a/test/timetest.c b/test/timetest.c +index 18c0546..cd52cdd 100644 +--- a/test/timetest.c ++++ b/test/timetest.c +@@ -19,6 +19,7 @@ + */ + + #define _GNU_SOURCE ++#include <fcntl.h> + #include <stdio.h> + #include <stdlib.h> + #include <time.h> +@@ -44,6 +45,69 @@ + + #define SIG SIGUSR1 + ++static void test_utimens_now(void) ++{ ++ char path[] = "/tmp/libfaketime-utimensat-XXXXXX"; ++ struct timespec now_times[2]; ++ int fd; ++ ++ fd = mkstemp(path); ++ if (fd == -1) ++ { ++ perror("mkstemp"); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (utimensat(AT_FDCWD, path, NULL, 0) == -1) ++ { ++ perror("utimensat(NULL)"); ++ unlink(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (futimens(fd, NULL) == -1) ++ { ++ perror("futimens(NULL)"); ++ close(fd); ++ unlink(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ now_times[0].tv_sec = now_times[1].tv_sec = 0; ++ now_times[0].tv_nsec = now_times[1].tv_nsec = UTIME_NOW; ++ ++ if (utimensat(AT_FDCWD, path, now_times, 0) == -1) ++ { ++ perror("utimensat(UTIME_NOW)"); ++ close(fd); ++ unlink(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (futimens(fd, now_times) == -1) ++ { ++ perror("futimens(UTIME_NOW)"); ++ close(fd); ++ unlink(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (close(fd) == -1) ++ { ++ perror("close"); ++ unlink(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (unlink(path) == -1) ++ { ++ perror("unlink"); ++ exit(EXIT_FAILURE); ++ } ++ ++ printf("utimensat()/futimens(): NOW handling passed\n"); ++} ++ + static void + handler(int sig, siginfo_t *si, void *uc) + { +@@ -237,6 +301,7 @@ printf("%s", 0 == 1 ? argv[0] : ""); + printf("gettimeofday() : Current date and time: %s", ctime(&tv.tv_sec)); + + #ifndef __APPLE__ ++ test_utimens_now(); + if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) + { + perror("sigprocmask"); diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/fix-utimensat-futimens-fake-now-handling.patch faketime-0.9.12/debian/patches/fix-utimensat-futimens-fake-now-handling.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/fix-utimensat-futimens-fake-now-handling.patch 1969-12-31 19:00:00.000000000 -0500 +++ faketime-0.9.12/debian/patches/fix-utimensat-futimens-fake-now-handling.patch 2026-03-28 13:53:05.000000000 -0400 @@ -0,0 +1,103 @@ +From: Aquila Macedo <[email protected]> +Date: Fri, 27 Mar 2026 16:04:18 -0300 +Subject: Fix utimensat/futimens handling of fake "now" +Origin: upstream, e8a1c1fd3b265db7071823398591dc592ec8e65d + +Use fake_clock_gettime(CLOCK_REALTIME) to resolve NULL/UTIME_NOW +timestamps instead of reconstructing them from user_offset. This +avoids invalid timestamp handling in start-at faketime mode, where +"now" is derived from the configured base time plus elapsed real time. + +Closes: #535 +--- + src/libfaketime.c | 38 +++++++++++++++++++++++++++++++------- + 1 file changed, 31 insertions(+), 7 deletions(-) + +diff --git a/src/libfaketime.c b/src/libfaketime.c +index 6d1a575..0e3d1ce 100644 +--- a/src/libfaketime.c ++++ b/src/libfaketime.c +@@ -1243,9 +1243,22 @@ int utimes(const char *filename, const struct timeval times[2]) + return result; + } + ++static int fake_current_realtime(struct timespec *tp) ++{ ++ int result; ++ ++ DONT_FAKE_TIME(result = (*real_clock_gettime)(CLOCK_REALTIME, tp)); ++ if (result == -1) ++ { ++ return -1; ++ } ++ ++ return fake_clock_gettime(CLOCK_REALTIME, tp); ++} ++ + /* This conditionally offsets 2 timespec values. The caller's out_times array + * always contains valid translated values, even if in_times was NULL. */ +-static void fake_two_timespec(const struct timespec in_times[2], struct timespec out_times[2]) ++static int fake_two_timespec(const struct timespec in_times[2], struct timespec out_times[2]) + { + if (in_times == NULL) /* Translate NULL into 2 UTIME_NOW values */ + { +@@ -1254,7 +1267,7 @@ static void fake_two_timespec(const struct timespec in_times[2], struct timespec + in_times = out_times; + } + struct timespec now; +- now.tv_nsec = UTIME_OMIT; /* Wait to grab the current time to see if it's actually needed */ ++ int have_fake_now = 0; + int j; + for (j = 0; j <= 1; j++) + { +@@ -1263,11 +1276,15 @@ static void fake_two_timespec(const struct timespec in_times[2], struct timespec + { + if (fake_utime_disabled && in_times[j].tv_nsec == UTIME_NOW) + { /* The user wants their given fake times left alone but they requested NOW, so turn it into fake NOW */ +- if (now.tv_nsec == UTIME_OMIT) /* did we grab "now" yet? */ ++ if (!have_fake_now) + { +- DONT_FAKE_TIME(real_clock_gettime(CLOCK_REALTIME, &now)); ++ if (fake_current_realtime(&now) == -1) ++ { ++ return -1; ++ } ++ have_fake_now = 1; + } +- timeradd2(&now, &user_offset, &out_times[j], n); ++ out_times[j] = now; + } + else if (out_times != in_times) + { /* Just preserve the input value */ +@@ -1279,6 +1296,7 @@ static void fake_two_timespec(const struct timespec in_times[2], struct timespec + timersub2(&in_times[j], &user_offset, &out_times[j], n); + } + } ++ return 0; + } + + #ifdef MACOS_DYLD_INTERPOSE +@@ -1292,7 +1310,10 @@ int utimensat(int dirfd, const char *filename, const struct timespec times[2], i + + int result; + struct timespec tn[2]; +- fake_two_timespec(times, tn); ++ if (fake_two_timespec(times, tn) == -1) ++ { ++ return -1; ++ } + #ifdef MACOS_DYLD_INTERPOSE + DONT_FAKE_TIME(result = utimensat(dirfd, filename, tn, flags)); + #else +@@ -1312,7 +1333,10 @@ int futimens(int fd, const struct timespec times[2]) + + int result; + struct timespec tn[2]; +- fake_two_timespec(times, tn); ++ if (fake_two_timespec(times, tn) == -1) ++ { ++ return -1; ++ } + #ifdef MACOS_DYLD_INTERPOSE + DONT_FAKE_TIME(result = futimens(fd, tn)); + #else diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/interpose-clock_gettime64.patch faketime-0.9.12/debian/patches/interpose-clock_gettime64.patch --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/interpose-clock_gettime64.patch 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/interpose-clock_gettime64.patch 1969-12-31 19:00:00.000000000 -0500 @@ -1,86 +0,0 @@ -From: Helge Deller <[email protected]> -Date: Sun, 12 Jan 2025 22:23:16 +0000 -Subject: Interpose clock_gettime64 - -Since debian generally added 64-bit time support on 32-bit -arches, now glibc sometimes calls the clock_gettime64 syscall -(and library wrapper). This function was missing, and is added here. - -Closes: #1064555 - - -This patch is part of an upstream MR: - https://github.com/wolfcw/libfaketime/pull/487 ---- - src/libfaketime.c | 24 ++++++++++++++++++++++++ - test/Makefile | 2 +- - 2 files changed, 25 insertions(+), 1 deletion(-) - -diff --git a/src/libfaketime.c b/src/libfaketime.c -index c59c122..cb37bcf 100644 ---- a/src/libfaketime.c -+++ b/src/libfaketime.c -@@ -164,6 +164,13 @@ struct utimbuf { - #include <sys/random.h> - #endif - -+/* __timespec64 is needed for clock_gettime64 on 32-bit architectures */ -+struct __timespec64 -+{ -+ uint64_t tv_sec; /* Seconds */ -+ uint64_t tv_nsec; /* Nanoseconds */ -+}; -+ - /* - * Per thread variable, which we turn on inside real_* calls to avoid modifying - * time multiple times of for the whole process to prevent faking time -@@ -201,6 +208,7 @@ static time_t (*real_time) (time_t *); - static int (*real_ftime) (struct timeb *); - static int (*real_gettimeofday) (struct timeval *, void *); - static int (*real_clock_gettime) (clockid_t clk_id, struct timespec *tp); -+static int (*real_clock_gettime64) (clockid_t clk_id, struct __timespec64 *tp); - #ifdef TIME_UTC - static int (*real_timespec_get) (struct timespec *ts, int base); - #endif -@@ -2417,6 +2425,17 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp) - return result; - } - -+/* this is used by 32-bit architectures only */ -+int __clock_gettime64(clockid_t clk_id, struct __timespec64 *tp64) -+{ -+ struct timespec tp; -+ int result; -+ -+ result = clock_gettime(clk_id, &tp); -+ tp64->tv_sec = tp.tv_sec; -+ tp64->tv_nsec = tp.tv_nsec; -+ return result; -+} - - #ifdef TIME_UTC - #ifdef MACOS_DYLD_INTERPOSE -@@ -2760,6 +2779,11 @@ static void ftpl_really_init(void) - { - real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime"); - } -+ real_clock_gettime64 = dlsym(RTLD_NEXT, "clock_gettime64"); -+ if (NULL == real_clock_gettime64) -+ { -+ real_clock_gettime64 = dlsym(RTLD_NEXT, "__clock_gettime64"); -+ } - #ifdef FAKE_TIMERS - #if defined(__sun) - real_timer_gettime_233 = dlsym(RTLD_NEXT, "timer_gettime"); -diff --git a/test/Makefile b/test/Makefile -index 763ebc4..01cbd9f 100644 ---- a/test/Makefile -+++ b/test/Makefile -@@ -1,6 +1,6 @@ - CC = gcc - --CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) -+CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) -U_FILE_OFFSET_BITS -U_TIME_BITS - LDFLAGS += -lrt -lpthread - - SRC = timetest.c diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/series faketime-0.9.12/debian/patches/series --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/patches/series 2025-01-27 08:14:40.000000000 -0500 +++ faketime-0.9.12/debian/patches/series 2026-03-28 13:53:05.000000000 -0400 @@ -1,10 +1,2 @@ -interpose-clock_gettime64.patch -0002-Fix-interposition-of-clock_gettime64.patch -0003-Interpose-__time64.patch -0004-Interpose-gettimeofday64.patch -0005-Re-disable-faking-utime-by-default.patch -0006-Replace-data-race-with-use-of-pthread_once-ftpl_init.patch -0007-Replace-data-race-with-use-of-pthread_once-ft_shm_in.patch -0008-Call-ftpl_init-before-using-monotonic_conds_lock.patch -0009-Don-t-use-_try_-locking-calls-for-monotonic_conds_lo.patch -0010-485.patch +fix-utimensat-futimens-fake-now-handling.patch +add-regression-test-for-utimens-now.patch diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/debian/salsa-ci.yml faketime-0.9.12/debian/salsa-ci.yml --- faketime-0.9.10+2024-06-05+gba9ed5b2/debian/salsa-ci.yml 1969-12-31 19:00:00.000000000 -0500 +++ faketime-0.9.12/debian/salsa-ci.yml 2026-03-28 13:53:05.000000000 -0400 @@ -0,0 +1,3 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/.github/workflows/make-test.yml faketime-0.9.12/.github/workflows/make-test.yml --- faketime-0.9.10+2024-06-05+gba9ed5b2/.github/workflows/make-test.yml 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/.github/workflows/make-test.yml 2025-06-09 08:31:15.000000000 -0400 @@ -12,7 +12,7 @@ build: strategy: matrix: - os: [ubuntu-latest, macOS-latest] + os: [ubuntu-latest, ubuntu-22.04] runs-on: ${{ matrix.os }} steps: diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/Makefile faketime-0.9.12/Makefile --- faketime-0.9.10+2024-06-05+gba9ed5b2/Makefile 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/Makefile 2025-06-09 08:31:15.000000000 -0400 @@ -33,23 +33,4 @@ $(MAKE) $(SELECTOR) -C src distclean $(MAKE) $(SELECTOR) -C test distclean -macarm64: - $(MAKE) $(SELECTOR) -C src clean - $(MAKE) $(SELECTOR) -C src distclean - $(MAKE) $(SELECTOR) -C src all -# $(MAKE) $(SELECTOR) -C test all -# $(MAKE) $(SELECTOR) -C test distclean - $(MAKE) $(SELECTOR) -C src install - $(MAKE) $(SELECTOR) -C man install - $(INSTALL) -dm0755 "${DESTDIR}${PREFIX}/share/doc/faketime/" - $(INSTALL) -m0644 README "${DESTDIR}${PREFIX}/share/doc/faketime/README" - $(INSTALL) -m0644 NEWS "${DESTDIR}${PREFIX}/share/doc/faketime/NEWS" - -macarm64full: - $(MAKE) $(SELECTOR) -C src clean - $(MAKE) $(SELECTOR) -C src distclean - $(MAKE) $(SELECTOR) -C src all - $(MAKE) $(SELECTOR) -C test all -# $(MAKE) $(SELECTOR) -C test distclean - .PHONY: all test install uninstall clean distclean diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/man/faketime.1 faketime-0.9.12/man/faketime.1 --- faketime-0.9.10+2024-06-05+gba9ed5b2/man/faketime.1 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/man/faketime.1 2025-06-09 08:31:15.000000000 -0400 @@ -1,4 +1,4 @@ -.TH FAKETIME "1" "March 2022" "faketime 0.9.10" wolfcw +.TH FAKETIME "1" "June 2025" "faketime 0.9.12" wolfcw .SH NAME faketime \- manipulate the system time for a given command .SH SYNOPSIS diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/NEWS faketime-0.9.12/NEWS --- faketime-0.9.10+2024-06-05+gba9ed5b2/NEWS 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/NEWS 2025-06-09 08:31:15.000000000 -0400 @@ -1,3 +1,20 @@ +List of changes for v0.9.12 +=========================== + +Since 0.9.11: + - Improved macOS compatibility (@usertam) + +Since 0.9.10: + - Fixed various cross-platform compile-time issues + - Honor nanosecond parameters/fields in relevant system calls + - Limited improvements to enhance compatibility with other + LD_PRELOAD libraries + - Added selected more intercepted system calls + - Unset FAKETIME_SHARED automatically for child processes + when enabling FAKETIME_FLSHM=1 + - Disable shared memory for child processes through + FAKETIME_DISABLE_SHM=1 + Since 0.9.9: - automatically try to decide about FORCE_MONOTONIC_FIX at run-time when not set as a compile-time flag diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/README faketime-0.9.12/README --- faketime-0.9.10+2024-06-05+gba9ed5b2/README 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/README 2025-06-09 08:31:15.000000000 -0400 @@ -1,5 +1,5 @@ -libfaketime, version 0.9.10 (March 2022) -======================================== +libfaketime, version 0.9.12 (June 2025) +======================================= Content of this file: diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/README.OSX faketime-0.9.12/README.OSX --- faketime-0.9.10+2024-06-05+gba9ed5b2/README.OSX 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/README.OSX 2025-06-09 08:31:15.000000000 -0400 @@ -166,3 +166,50 @@ and always takes precedence over other user-controlled settings. It can be re-set to 0 (zero) to work around potential incompatibilities or if you do not want libfaketime applied to your software. + +5) Working with the new arm64e system binaries in Apple Silicon +--------------------------------------------------------------- + +Since Apple Silicon, Apple started shipping system binaries compiled against +the `arm64e` ABI. This new ABI enforces Pointer Authentication Codes (PACs), +and enforces assembly instructions to sign and check pointer signatures to +prevent malicious control flow altering. + + $ file /bin/date + /bin/date: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e] + /bin/date (for architecture x86_64): Mach-O 64-bit executable x86_64 + /bin/date (for architecture arm64e): Mach-O 64-bit executable arm64e + +Most importantly, the new `arm64e` ABI is incompatible with the normal `arm64` +ABI we are used to; this is done so that everything `arm64e` is PAC-enforced. + +As a result, this will happen when we try to hook naive `arm64` libfaketime on +system binaries (and vice versa with `arm64e` libfaketime on `arm64` binaries): + + $ DYLD_INSERT_LIBRARIES=libfaketime.1.dylib /bin/date + dyld[5788]: terminating because inserted dylib 'libfaketime.1.dylib' could not be loaded: + tried: 'libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')) + +Since PR #497, we now compile libfaketime with a fat library/binary setup, so +that we support both ABIs at the same time: + + $ file libfaketime.1.dylib + libfaketime.1.dylib: Mach-O universal binary with 2 architectures: [arm64:Mach-O 64-bit dynamically linked shared library arm64] [arm64e:Mach-O 64-bit dynamically linked shared library arm64e] + libfaketime.1.dylib (for architecture arm64): Mach-O 64-bit dynamically linked shared library arm64 + libfaketime.1.dylib (for architecture arm64e): Mach-O 64-bit dynamically linked shared library arm64e + +Unfortunately, Apple does not support running third-party `arm64e` code yet, +since the ABI is still unstable. This means that you cannot use libfaketime +on system `arm64e` binaries out of the box, at the time of writing. + +If you really need to, you may disable SIP in the recovery terminal: + + (in recovery) # csrutil disable + +And enable the experimental ABI after boot: + + (in regular boot) $ sudo nvram boot-args=-arm64e_preview_abi + +Then `arm64e` should work as-is. This use case is rather uncommon since most +userspace binaries will remain `arm64` for the time being, until Apple really +doubles down on `arm64e`. Regardless, we should be prepared for that. diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/src/faketime.c faketime-0.9.12/src/faketime.c --- faketime-0.9.10+2024-06-05+gba9ed5b2/src/faketime.c 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/src/faketime.c 2025-06-09 08:31:15.000000000 -0400 @@ -1,7 +1,7 @@ /* * libfaketime wrapper command * - * This file is part of libfaketime, version 0.9.10 + * This file is part of libfaketime, version 0.9.12 * * libfaketime is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the @@ -48,7 +48,7 @@ #include "faketime_common.h" -const char version[] = "0.9.10"; +const char version[] = "0.9.12"; #if (defined __APPLE__) || (defined __sun) static const char *date_cmd = "gdate"; diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/src/libfaketime.c faketime-0.9.12/src/libfaketime.c --- faketime-0.9.10+2024-06-05+gba9ed5b2/src/libfaketime.c 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/src/libfaketime.c 2025-06-09 08:31:15.000000000 -0400 @@ -1,5 +1,5 @@ /* - * This file is part of libfaketime, version 0.9.10 + * This file is part of libfaketime, version 0.9.12 * * libfaketime is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the @@ -42,6 +42,7 @@ #endif #include <time.h> #ifdef MACOS_DYLD_INTERPOSE +#include <pthread.h> #include <sys/time.h> #include <utime.h> #endif @@ -58,6 +59,9 @@ #ifdef __linux__ #include <stdarg.h> #include <sys/syscall.h> +#ifdef INTERCEPT_FUTEX +#include <linux/futex.h> +#endif #else #error INTERCEPT_SYSCALL should only be defined on GNU/Linux systems. #endif @@ -164,6 +168,20 @@ #include <sys/random.h> #endif +/* __timespec64 is needed for clock_gettime64 on 32-bit architectures */ +struct __timespec64 +{ + uint64_t tv_sec; /* Seconds */ + uint32_t tv_nsec; /* this is 32-bit, apparently! */ +}; + +/* __timespec64 is needed for clock_gettime64 on 32-bit architectures */ +struct __timeval64 +{ + uint64_t tv_sec; /* Seconds */ + uint64_t tv_usec; /* this is 64-bit, apparently! */ +}; + /* * Per thread variable, which we turn on inside real_* calls to avoid modifying * time multiple times of for the whole process to prevent faking time @@ -190,10 +208,12 @@ static int (*real_fxstat) (int, int, struct stat *); static int (*real_fxstatat) (int, int, const char *, struct stat *, int); static int (*real_lxstat) (int, const char *, struct stat *); +#if !defined(__APPLE__) || !__DARWIN_ONLY_64_BIT_INO_T static int (*real_xstat64) (int, const char *, struct stat64 *); static int (*real_fxstat64) (int, int , struct stat64 *); static int (*real_fxstatat64) (int, int , const char *, struct stat64 *, int); static int (*real_lxstat64) (int, const char *, struct stat64 *); +#endif #ifdef STATX_TYPE static int (*real_statx) (int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf); #endif @@ -201,6 +221,7 @@ static int (*real_ftime) (struct timeb *); static int (*real_gettimeofday) (struct timeval *, void *); static int (*real_clock_gettime) (clockid_t clk_id, struct timespec *tp); +static int (*real_clock_gettime64) (clockid_t clk_id, struct __timespec64 *tp); #ifdef TIME_UTC static int (*real_timespec_get) (struct timespec *ts, int base); #endif @@ -217,7 +238,7 @@ static pthread_rwlock_t monotonic_conds_lock; #endif -#ifndef __APPLEOSX__ +#ifndef __APPLE__ #ifdef FAKE_TIMERS static int (*real_timer_settime_22) (int timerid, int flags, const struct itimerspec *new_value, struct itimerspec * old_value); @@ -263,7 +284,7 @@ static int (*real_sem_timedwait) (sem_t*, const struct timespec*); static int (*real_sem_clockwait) (sem_t *sem, clockid_t clockid, const struct timespec *abstime); #endif -#ifdef __APPLEOSX__ +#ifdef __APPLE__ static int (*real_clock_get_time) (clock_serv_t clock_serv, mach_timespec_t *cur_timeclockid_t); static int apple_clock_gettime (clockid_t clk_id, struct timespec *tp); static clock_serv_t clock_serv_real; @@ -304,7 +325,7 @@ #define CHECK_MISSING_REAL(name) \ check_missing_real(#name, (NULL == real_##name)) -static int initialized = 0; +static pthread_once_t initialized_once_control = PTHREAD_ONCE_INIT; /* prototypes */ static int fake_gettimeofday(struct timeval *tv); @@ -521,16 +542,20 @@ } } +static pthread_once_t ft_shm_initialized_once_control = PTHREAD_ONCE_INIT; + +static void ft_shm_really_init (void); static void ft_shm_init (void) { + pthread_once(&ft_shm_initialized_once_control, ft_shm_really_init); +} + +static void ft_shm_really_init (void) +{ int ticks_shm_fd; char sem_name[256], shm_name[256], *ft_shared_env = getenv("FAKETIME_SHARED"); sem_t *shared_semR = NULL; static int nt=1; - static int ft_shm_initialized = 0; - - /* do all of this once only */ - if (ft_shm_initialized > 0) return; /* create semaphore and shared memory locally unless it has been passed along */ if (ft_shared_env == NULL) @@ -610,8 +635,6 @@ { /* force the deletion of the shm sync env variable */ unsetenv("FAKETIME_SHARED"); } - - ft_shm_initialized = 1; } static void ft_cleanup (void) @@ -673,7 +696,7 @@ /* Get system time from system for all clocks */ static void system_time_from_system (struct system_time_s * systime) { -#ifdef __APPLEOSX__ +#ifdef __APPLE__ /* from https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x */ clock_serv_t cclock; mach_timespec_t mts; @@ -876,7 +899,7 @@ * ======================================================================= */ #ifdef FAKE_UTIME -static int fake_utime_disabled = 0; +static int fake_utime_disabled = 1; #endif @@ -962,6 +985,7 @@ #endif } +#ifndef __APPLE__ static inline void fake_stat64buf (struct stat64 *buf) { #ifndef st_atime lock_for_stat(); @@ -971,18 +995,13 @@ unlock_for_stat(); #else lock_for_stat(); -#ifndef __APPLE__ fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctim); fake_clock_gettime(CLOCK_REALTIME, &buf->st_atim); fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtim); -#else - fake_clock_gettime(CLOCK_REALTIME, &buf->st_ctimespec); - fake_clock_gettime(CLOCK_REALTIME, &buf->st_atimespec); - fake_clock_gettime(CLOCK_REALTIME, &buf->st_mtimespec); -#endif unlock_for_stat(); #endif } +#endif /* macOS dyld interposing uses the function's real name instead of real_name */ #ifdef MACOS_DYLD_INTERPOSE @@ -1092,25 +1111,19 @@ { STAT_HANDLER(lxstat, buf, ver, path, buf); } -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __xstat64 (int ver, const char *path, struct stat64 *buf) { STAT64_HANDLER(xstat64, buf, ver, path, buf); } -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __fxstat64 (int ver, int fildes, struct stat64 *buf) { STAT64_HANDLER(fxstat64, buf, ver, fildes, buf); } -#endif -#ifndef __APPLE__ /* Added in v0.8 as suggested by Daniel Kahn Gillmor */ #ifndef NO_ATFILE int __fxstatat64 (int ver, int fildes, const char *filename, struct stat64 *buf, int flag) @@ -1118,16 +1131,14 @@ STAT64_HANDLER(fxstatat64, buf, ver, fildes, filename, buf, flag); } #endif -#endif -#ifndef __APPLE__ /* Contributed by Philipp Hachtmann in version 0.6 */ int __lxstat64 (int ver, const char *path, struct stat64 *buf) { STAT64_HANDLER(lxstat64, buf, ver, path, buf); } -#endif -#endif +#endif /* ifndef __APPLE__ */ +#endif /* ifdef FAKE_STAT */ #ifdef STATX_TYPE static inline void fake_statx_timestamp(struct statx_timestamp* p) @@ -2346,44 +2357,12 @@ #endif { int result; - static int recursion_depth = 0; - if (!initialized) - { - recursion_depth++; -#ifdef FAIL_PRE_INIT_CALLS - fprintf(stderr, "libfaketime: clock_gettime() was called before initialization.\n"); - fprintf(stderr, "libfaketime: Returning -1 on clock_gettime().\n"); - if (tp != NULL) - { - tp->tv_sec = 0; - tp->tv_nsec = 0; - } - return -1; -#else - if (recursion_depth == 2) - { - fprintf(stderr, "libfaketime: Unexpected recursive calls to clock_gettime() without proper initialization. Trying alternative.\n"); - DONT_FAKE_TIME(ftpl_init()) ; - } - else if (recursion_depth == 3) - { - fprintf(stderr, "libfaketime: Cannot recover from unexpected recursive calls to clock_gettime().\n"); - fprintf(stderr, "libfaketime: Please check whether any other libraries are in use that clash with libfaketime.\n"); - fprintf(stderr, "libfaketime: Returning -1 on clock_gettime() to break recursion now... if that does not work, please check other libraries' error handling.\n"); - if (tp != NULL) - { - tp->tv_sec = 0; - tp->tv_nsec = 0; - } - return -1; - } - else { - ftpl_init(); - } -#endif - recursion_depth--; - } + ftpl_init(); + // If ftpl_init ends up recursing, pthread_once will deadlock. + // (Previously we attempted to detect this situation, and bomb out, + // but the approach taken wasn't thread-safe and broke in practice.) + /* sanity check */ if (tp == NULL) { @@ -2417,6 +2396,50 @@ return result; } +/* this is used by 32-bit architectures only */ +int __clock_gettime64(clockid_t clk_id, struct __timespec64 *tp64) +{ + struct timespec tp; + int result; + + result = clock_gettime(clk_id, &tp); + tp64->tv_sec = tp.tv_sec; + tp64->tv_nsec = tp.tv_nsec; + return result; +} + +/* this is used by 32-bit architectures only */ +int __gettimeofday64(struct __timeval64 *tv64, void *tz) +{ + struct timeval tv; + int result; + + result = gettimeofday(&tv, tz); + tv64->tv_sec = tv.tv_sec; + tv64->tv_usec = tv.tv_usec; + return result; +} + +/* this is used by 32-bit architectures only */ +uint64_t __time64(uint64_t *write_out) +{ + struct timespec tp; + uint64_t output; + int error; + + error = clock_gettime(CLOCK_REALTIME, &tp); + if (error == -1) + { + return (uint64_t)error; + } + output = tp.tv_sec; + + if (write_out) + { + *write_out = output; + } + return output; +} #ifdef TIME_UTC #ifdef MACOS_DYLD_INTERPOSE @@ -2656,10 +2679,12 @@ real_fxstat = dlsym(RTLD_NEXT, "__fxstat"); real_fxstatat = dlsym(RTLD_NEXT, "__fxstatat"); real_lxstat = dlsym(RTLD_NEXT, "__lxstat"); +#if !defined(__APPLE__) || !__DARWIN_ONLY_64_BIT_INO_T real_xstat64 = dlsym(RTLD_NEXT,"__xstat64"); real_fxstat64 = dlsym(RTLD_NEXT, "__fxstat64"); real_fxstatat64 = dlsym(RTLD_NEXT, "__fxstatat64"); real_lxstat64 = dlsym(RTLD_NEXT, "__lxstat64"); +#endif #ifdef STATX_TYPE real_statx = dlsym(RTLD_NEXT, "statx"); #endif @@ -2751,7 +2776,7 @@ exit(-1); } #endif -#ifdef __APPLEOSX__ +#ifdef __APPLE__ real_clock_get_time = dlsym(RTLD_NEXT, "clock_get_time"); real_clock_gettime = apple_clock_gettime; #else @@ -2760,6 +2785,11 @@ { real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime"); } + real_clock_gettime64 = dlsym(RTLD_NEXT, "clock_gettime64"); + if (NULL == real_clock_gettime64) + { + real_clock_gettime64 = dlsym(RTLD_NEXT, "__clock_gettime64"); + } #ifdef FAKE_TIMERS #if defined(__sun) real_timer_gettime_233 = dlsym(RTLD_NEXT, "timer_gettime"); @@ -2795,7 +2825,6 @@ #undef dlsym #undef dlvsym - initialized = 1; #ifdef FAKE_STATELESS if (0) ft_shm_init(); @@ -3046,10 +3075,7 @@ } inline static void ftpl_init(void) { - if (!initialized) - { - ftpl_really_init(); - } + pthread_once(&initialized_once_control, ftpl_really_init); } void *ft_dlvsym(void *handle, const char *symbol, const char *version, @@ -3511,7 +3537,7 @@ * ======================================================================= */ -#ifdef __APPLEOSX__ +#ifdef __APPLE__ /* * clock_gettime implementation for __APPLE__ * @note It always behave like being called with CLOCK_REALTIME. @@ -3724,9 +3750,9 @@ struct pthread_cond_monotonic *e = (struct pthread_cond_monotonic*)malloc(sizeof(struct pthread_cond_monotonic)); e->ptr = cond; - if (pthread_rwlock_trywrlock(&monotonic_conds_lock) != 0) { - sched_yield(); - return EAGAIN; + if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) { + fprintf(stderr,"can't acquire write monotonic_conds_lock\n"); + exit(-1); } HASH_ADD_PTR(monotonic_conds, ptr, e); pthread_rwlock_unlock(&monotonic_conds_lock); @@ -3739,9 +3765,11 @@ { struct pthread_cond_monotonic* e; - if (pthread_rwlock_trywrlock(&monotonic_conds_lock) != 0) { - sched_yield(); - return EBUSY; + ftpl_init(); + + if (pthread_rwlock_wrlock(&monotonic_conds_lock) != 0) { + fprintf(stderr,"can't acquire write monotonic_conds_lock\n"); + exit(-1); } HASH_FIND_PTR(monotonic_conds, &cond, e); if (e) { @@ -3821,11 +3849,13 @@ clockid_t clk_id; int result = 0; + ftpl_init(); + if (abstime != NULL) { - if (pthread_rwlock_tryrdlock(&monotonic_conds_lock) != 0) { - sched_yield(); - return EAGAIN; + if (pthread_rwlock_rdlock(&monotonic_conds_lock) != 0) { + fprintf(stderr,"can't acquire read monotonic_conds_lock\n"); + exit(-1); } HASH_FIND_PTR(monotonic_conds, &cond, e); pthread_rwlock_unlock(&monotonic_conds_lock); @@ -4151,6 +4181,90 @@ #endif #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) { + if (timeout == NULL) { + // not timeout related, just call the real syscall + 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; + + struct timespec real_tp, fake_tp; + + DONT_FAKE_TIME((*real_clock_gettime)(clk_id, &real_tp)); + fake_tp = real_tp; + if (fake_clock_gettime(clk_id, &fake_tp) == -1) { + goto futex_fallback; + } + // Create a corrected timeout by adjusting with the difference between + // real and fake timestamps + 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); + if (result != 0) { + return result; + } + + // Check if the futex timeout has already passed according to fake time + struct timespec now_fake; + if (fake_clock_gettime(clk_id, &now_fake) != 0) { + return result; + } + + // If the timeout is already passed in fake time, return 0. + while (!timespeccmp(&now_fake, timeout, >=)) { + // Calculate how much real time we need to wait + struct timespec real_now, fake_now, wait_time; + DONT_FAKE_TIME((*real_clock_gettime)(clk_id, &real_now)); + fake_clock_gettime(clk_id, &fake_now); + + // Calculate how much fake time is left until the timeout + struct timespec fake_time_left; + timespecsub(timeout, &fake_now, &fake_time_left); + + // Scale the fake time left by the user rate if set + if (user_rate_set && !dont_fake) { + timespecmul(&fake_time_left, 1.0 / user_rate, &wait_time); + } else { + wait_time = fake_time_left; + } + + // Calculate the real timeout by adding the wait time to the current real time + 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); + if (result != 0) { + return result; + } + + // Check if the futex timeout has already passed according to fake time + if (fake_clock_gettime(clk_id, &now_fake) != 0) { + return result; + } + } + return 0; + } else { + return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3); + } + + futex_fallback: + return real_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3); +} +#endif + /* see https://github.com/wolfcw/libfaketime/issues/301 */ long syscall(long number, ...) { va_list ap; @@ -4177,6 +4291,27 @@ return clock_gettime(clk_id, tp); } +#ifdef INTERCEPT_FUTEX + if (number == __NR_futex) { + uint32_t *uaddr; + int futex_op; + uint32_t val; + struct timespec *timeout; /* or: uint32_t val2 */ + uint32_t* uaddr2; + uint32_t val3; + + uaddr = va_arg(ap, uint32_t*); + futex_op = va_arg(ap, int); + val = va_arg(ap, uint32_t); + timeout = va_arg(ap, struct timespec*); + uaddr2 = va_arg(ap, uint32_t*); + val3 = va_arg(ap, uint32_t); + va_end(ap); + + return handle_futex_syscall(number, uaddr, futex_op, val, timeout, uaddr2, val3); + } +#endif + variadic_promotion_t a[syscall_max_args]; for (int i = 0; i < syscall_max_args; i++) a[i] = va_arg(ap, variadic_promotion_t); diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/src/Makefile.OSX faketime-0.9.12/src/Makefile.OSX --- faketime-0.9.10+2024-06-05+gba9ed5b2/src/Makefile.OSX 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/src/Makefile.OSX 2025-06-09 08:31:15.000000000 -0400 @@ -56,21 +56,24 @@ PREFIX ?= /usr/local CFLAGS += -DFAKE_SLEEP -DFAKE_INTERNAL_CALLS -DPREFIX='"'${PREFIX}'"' $(FAKETIME_COMPILE_CFLAGS) -DMACOS_DYLD_INTERPOSE -DFAKE_SETTIME -LIB_LDFLAGS += -dynamiclib -current_version 0.9.10 -compatibility_version 0.7 +LIB_LDFLAGS += -dynamiclib -current_version 0.9.12 -compatibility_version 0.7 -# ARM64 MacOS (M1/M2/M3/Apple Silicon/etc) processors require a target set as their current version, or they -# will receive the following error: -# dyld[6675]: terminating because inserted dylib '/usr/local/lib/faketime/libfaketime.1.dylib' could not be loaded: tried: '/usr/local/lib/faketime/libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/lib/faketime/libfaketime.1.dylib' (no such file), '/usr/local/lib/faketime/libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')) -# dyld[6675]: tried: '/usr/local/lib/faketime/libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/lib/faketime/libfaketime.1.dylib' (no such file), '/usr/local/lib/faketime/libfaketime.1.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e')) -# Outputs `arm64` on ARM64 -OS := $(shell uname -m) -# Outputs a number, eg 14.4 for MacOS Sonoma 14.4 -MACOS_PRODUCT_VERSION := $(shell sw_vers --productVersion | cut -d. -f1,2) - -# Check if arm64 is in OS, if so, add the target -ifeq ($(OS),arm64) - CFLAGS += -target arm64e-apple-macos$(MACOS_PRODUCT_VERSION) - LIB_LDFLAGS += -target arm64e-apple-macos$(MACOS_PRODUCT_VERSION) +# From macOS 13 onwards, system binaries are compiled against the new arm64e ABI on Apple Silicon. +# These arm64e binaries enforce Pointer Authentication Code (PAC), and will refuse to run with +# "unprotected" arm64 libraries. Meanwhile, older platforms might not recognize the new arm64e ABI. + +# Therefore, we now compile for two ABIs at the same time, producing a fat library of arm64e and arm64, +# so in the end the OS gets to pick which architecture it wants at runtime. + +# In addition, we need to enable signing and authentication of indirect calls (-fptrauth-calls); +# otherwise in ftpl_init, pthread_once will indirectly call ftpl_really_init, which then fail PAC. +# Ideally this should be a compiler default for the arm64e ABI, but apparently not. + +ARCH := $(shell uname -m) + +ifeq ($(ARCH),arm64) + CFLAGS += -arch arm64e -arch arm64 + CFLAGS += -fptrauth-calls -fptrauth-returns endif SONAME = 1 diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/test/libmallocintercept.c faketime-0.9.12/test/libmallocintercept.c --- faketime-0.9.10+2024-06-05+gba9ed5b2/test/libmallocintercept.c 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/test/libmallocintercept.c 2025-06-09 08:31:15.000000000 -0400 @@ -24,7 +24,9 @@ #include <unistd.h> static void print_msg(const char *msg) { - write(0, msg, strlen(msg)); + size_t out; + out = write(0, msg, strlen(msg)); + (void) out; /* unused */ } static void* actual_malloc(size_t size) { diff -Nru faketime-0.9.10+2024-06-05+gba9ed5b2/test/Makefile faketime-0.9.12/test/Makefile --- faketime-0.9.10+2024-06-05+gba9ed5b2/test/Makefile 2024-06-05 13:52:44.000000000 -0400 +++ faketime-0.9.12/test/Makefile 2025-06-09 08:31:15.000000000 -0400 @@ -1,6 +1,6 @@ CC = gcc -CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) +CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) -U_FILE_OFFSET_BITS -U_TIME_BITS LDFLAGS += -lrt -lpthread SRC = timetest.c

