145 Commits

Author SHA1 Message Date
Wolfgang Hommel
2ba77bd61b Merge pull request #540 from AbhiTheModder/android
Add Android compatibility
2026-05-03 14:15:25 +02:00
Abhi
e36ec5726e Add Android compatibility
Adds support for both cross-compilation & native compilation (on Termux)
2026-05-02 18:41:32 +05:30
Wolfgang Hommel
37c9430117 Merge pull request #537 from aquilamacedo/follow-up-535
follow-up to #535: Fix the actual dpkg-source failure path
2026-03-30 19:38:19 +02:00
Aquila Macedo
4aa0077bfc Add regression coverage for utime and utimes "now" handling
Extend timetest to exercise utime(path, NULL) and utimes(path, NULL), so
the older file timestamp wrappers are covered alongside the existing
utimensat()/futimens() "set to now" checks.
2026-03-30 10:42:47 -03:00
Aquila Macedo
097ce79771 Fix fake "now" handling for utime and utimes
Resolve utime(..., NULL) and utimes(..., NULL) through the current fake
CLOCK_REALTIME instead of deriving "now" from local timeval state.

This matches the real dpkg-source failure mode seen under
FAKETIME='@2037-01-01 00:00:00', where touching .pc/applied-patches
ended up reaching utimensat() with an invalid explicit timespec and
failing with EINVAL.

Follow-up to #535
2026-03-30 10:42:17 -03:00
Wolfgang Hommel
994b7c75ca Merge pull request #536 from aquilamacedo/fix-535-issue-einval-under-start-at
Fix utimensat/futimens fake "now" handling in start-at mode
2026-03-28 11:05:23 +01:00
Aquila Macedo
483a7a703c Add regression test for utimensat/futimens now handling
Exercise the NULL and UTIME_NOW timestamp paths in timetest so the
file-timestamp "set to now" case is covered by the test suite.
2026-03-27 19:23:49 -03:00
Aquila Macedo
e8a1c1fd3b Fix utimensat/futimens handling of fake "now"
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
2026-03-27 19:23:35 -03:00
Wolfgang Hommel
75d91ea726 Merge pull request #533 from GeneralDisarray/implement-faketime-follow-absolute
Implement FAKETIME_FOLLOW_ABSOLUTE feature
2026-02-18 07:49:19 +01:00
Mike Rushe
6c7aa3966c Implement FAKETIME_FOLLOW_ABSOLUTE feature
See README for feature intent.
Simple regression test at test_follow_absolute.sh.
2026-02-17 12:17:48 -05:00
Wolfgang Hommel
3062fb2004 Merge pull request #530 from drolevar/fix_master
Fix several multi-arch-related issues
2026-02-01 12:17:06 +01:00
Andrij Abyzov
b687b165b2 Replace POSIX semaphore with flock() for cross-arch safety
POSIX named semaphores (sem_t) have architecture-dependent internal
layout in glibc: 32 bytes on 64-bit, 16 bytes on 32-bit. When a
64-bit faketime wrapper creates a semaphore and spawns a 32-bit child,
the child misinterprets the counter and hangs on sem_wait forever.

Extract ft_sem_* abstraction into shared ft_sem.h/ft_sem.c with three
backends: FT_POSIX (existing), FT_SYSV (existing), FT_FLOCK (new
default). The flock backend uses kernel-mediated file locking on
/dev/shm/faketime_lock_<pid>, which is architecture-independent and
auto-releases on process death.

Both libfaketime.so and the faketime wrapper now use the same shared
abstraction, ensuring protocol agreement regardless of backend.
2026-01-26 14:43:43 +01:00
Andrij Abyzov
e21bf3017a Fix cross-arch shared memory struct layout and ftruncate/munmap sizes
Replace struct timespec (arch-dependent long/time_t) with fixed-width
int64_t pairs in ft_shared_s so that 32-bit and 64-bit processes
interpret the same shared memory layout identically.

Fix ftruncate calls that allocated sizeof(uint64_t) (8 bytes) instead
of sizeof(struct ft_shared_s) (64-80 bytes) for the shared memory
region. Fix munmap in ft_cleanup using the wrong size.

Add struct layout test and cross-process shared memory functional test.
2026-01-26 13:31:55 +01:00
Wolfgang Hommel
13d47210d5 Merge pull request #528 from ppisar/gcc16
tests: Silence an unused-but-set-variable warning with GCC 16
2026-01-06 20:20:46 +01:00
Petr Písař
712733e5f0 tests: Silence an unused-but-set-variable warning with GCC 16
Compiling tests with GCC 16 results into this warning:

    gcc -shared -o libmallocintercept.so -fpic -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra  -U_FILE_OFFSET_BITS -U_TIME_BITS libmallocintercept.c
    libmallocintercept.c: In function ‘free’:
    libmallocintercept.c:79:12: error: variable ‘ptr2’ set but not used [-Werror=unused-but-set-variable=]
       79 |   long int ptr2 = (long int) ptr; ptr2 -= (long int) ptr;
	  |            ^~~~
    cc1: all warnings being treated as errors

The ptr2 variable was added in the past to silence compiler warnings,
probably for the very same reason (commits
75cbe8e507 and
2bfbe19f71). GCC 16 is smarter and
discovers that the ptr2 variables is not needed.

This patch changes the work around to "(void) unused_variable;" idiom
recommended by GCC manual and already used in print_msg().
2026-01-06 10:34:39 +01:00
Wolfgang Hommel
1e931cb4cd Merge pull request #527 from totoroyyb/master
fix: broken futex syscall when FUTEX_WAIT is used
2025-12-23 11:22:48 +01:00
Yibo Yan
026a2627af nits: remove warnings 2025-12-23 02:48:55 +00:00
Yibo Yan
7295f20288 mod: better error handling for edge cases 2025-12-23 01:26:35 +00:00
Yibo Yan
92bf909d95 fix: broken futex syscall when FUTEX_WAIT is used 2025-12-22 12:23:01 +00:00
Wolfgang Hommel
ebe76e26b0 Merge pull request #526 from rgacogne/fix-preload-hang-521
Fix: Better detection of recursive initialisation problems
2025-12-19 06:20:21 +01:00
Remi Gacogne
c0aa6189f7 Refactor ft_shm_init and ftpl_init to remove duplication
Signed-off-by: Remi Gacogne <remi.gacogne@powerdns.com>
2025-12-16 16:30:25 +01:00
Remi Gacogne
7ba95f4cb0 Fix: Better detection of recursive initialisation problems
Signed-off-by: Remi Gacogne <remi.gacogne@powerdns.com>
2025-12-16 11:50:40 +01:00
Wolfgang Hommel
aabe141783 Merge pull request #525 from TomasKorbar/master
Add const qualifiers to fix build with ISO C23
2025-12-15 12:51:49 +01:00
Tomas Korbar
dbe865dfdb Add const qualifiers to fix build with ISO C23
Fix https://github.com/wolfcw/libfaketime/issues/524
2025-12-15 11:03:21 +01:00
Wolfgang Hommel
1231a002e0 Merge pull request #522 from rbalint/fake-syscall-clock-nanosleep
Fake the clock_nanosleep syscall
2025-11-09 10:02:16 +01:00
Balint Reczey
3109728f45 Fake the clock_nanosleep syscall 2025-11-08 17:28:48 +01:00
Wolfgang Hommel
949b36e6a2 Merge pull request #519 from bjornfor/fix-semaphore-deletion
Only let the owner clean up semaphore and shared memory
2025-09-29 20:41:22 +02:00
Bjørn Forsman
0c76f27777 Only let the owner clean up semaphore and shared memory
I noticed a bug in the semaphore handling, when using the System V semaphore
backend:

  $ LD_PRELOAD=./src/libfaketime.so.1 bash -c "echo foo | sed s/foo/bar/"
  libfaketime: In lock_for_stat(), ft_sem_lock failed: Invalid argument
  [...exited with error...]

(Beware, the above command-line is not 100% deterministic; sometimes it
succeeds.)

Looking at the strace for the above command-line, it seems the bash echo
builtin process (or thread?) decides to remove the semaphore upon
exiting, while it's still in use by the sed process. sed then gets
EINVAL error ("Invalid argument") on its next semop call.

The root cause is a semantic difference between POSIX sem_unlink and
SysV semop(..., IPC_RMID), the two implementations for ft_sem_unlink:

* sem_unlink allows the semaphore to be used afterwards, as long as a
  process has a reference to the semaphore.
* semop(..., IPC_RMID) removes the semaphore immediately, and further
  use results in EINVAL error.

AFAICT, the simplest fix is to only let the owner of the semaphore (and
shared memory) do the clean up, which is what this patch does. Both
semaphore backends pass the tests with this change.
2025-09-29 13:41:57 +02:00
Wolfgang Hommel
7bfe6566b3 Merge pull request #516 from bjornfor/fix-dangling-pointer
Fix dangling pointer via ft_sem_t name field
2025-09-27 11:47:48 +02:00
Wolfgang Hommel
c2a9bc1878 Merge pull request #517 from bjornfor/portable-shebang
Use portable shebang
2025-09-27 11:47:19 +02:00
Bjørn Forsman
4d0f0b7426 doc: refer to commands by name, not absolute path
For simplicity and portability.

I left some instances in README.OSX as is, because I'm worried about
invalidating the docs.
2025-09-24 15:06:44 +02:00
Bjørn Forsman
9d63a80062 tests: replace /bin/bash with bash
For portability.
2025-09-24 15:03:34 +02:00
Bjørn Forsman
cccce3bf23 Replace #!/bin/bash shebangs with #!/usr/bin/env bash
For portability. E.g. /bin/bash doesn't exist on NixOS.
2025-09-24 15:02:44 +02:00
Bjørn Forsman
f33dda8022 Fix dangling pointer via ft_sem_t name field
ft_sem_create() is called with an argument located on the stack, which
means it's a bad idea to keep a reference to it in the 'name' field of
ft_sem_t -- the pointed to data goes out of scope and results in
unpredictable behaviour.

Fix it by making a copy of the semaphore name. Allocate a 256 char
buffer, to match existing code.

Fixes: 2649cdb156 ("Add semaphore abstraction layer")
2025-09-24 14:55:15 +02:00
Wolfgang Hommel
4fc06b90df Merge pull request #512 from sammytranGeo/correct-use-of-real-monotonic-clock
Use real clock monotonic when not faking
2025-08-23 19:33:26 +02:00
Sammy Tran
75e130c4f1 Use real clock monotonic when not faking 2025-08-13 15:15:12 -04:00
Wolfgang Hommel
066f38baac Merge pull request #510 from bjornfor/fix-musl-build
Only define stat64 when building with glibc
2025-08-07 13:14:29 +02:00
Bjørn Forsman
4de86c2145 Only define stat64 when building with glibc
musl defines stat64 as stat, leading to this build error:

  gcc -o libfaketime.o -c -std=gnu99 -Wall -Wextra -Werror -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'/nix/store/qpyvvrcas950da98mssw6ixlw7ckvyrb-libfaketime-0.9.11'"' -DLIBDIRNAME='"'/lib'"'  -Wno-nonnull-compare   libfaketime.c
  In file included from libfaketime.c:55:
  libfaketime.c:1276:5: error: redefinition of ‘stat’
   1276 | int stat64 (const char *path, struct stat64 *buf)
        |     ^~~~~~
  /nix/store/g9cgi4yyn5vrd1f9axj8gxdvwzv5ssvk-musl-1.2.5-dev/include/sys/stat.h:80:5: note: previous definition of ‘stat’ with type ‘int(const char *, struct stat *)’
     80 | int stat(const char *__restrict, struct stat *__restrict);
        |     ^~~~
  make[1]: *** [Makefile:161: libfaketime.o] Error 1

Fix it by only defining stat64 when building against glibc, since it's
not straight forward to detect musl, and it's the safest approach; there
might be other libc implementations that behave like musl.

Fixes: 53ba71e547 ("Handle stat64() call")
2025-08-07 09:19:13 +02:00
Wolfgang Hommel
ffdb51bc30 Merge pull request #507 from bjornfor/add-stat64
Handle stat64() call
2025-08-05 20:51:42 +02:00
Wolfgang Hommel
3aa2028174 Merge pull request #509 from lisanet/master
fixes issue #506 - build arch64 and arch64e separately
2025-08-03 14:27:27 +02:00
Simone Karin Lehmann
6566162e7e fix for issue #506 - build arch64 and arch64e separately and then lipo them. 2025-08-03 13:48:41 +02:00
Bjørn Forsman
53ba71e547 Handle stat64() call
This fixes missing modification of timestamps in stat() calls for
programs built with large file support (-D_FILE_OFFSET_BITS=64), both
32- and 64-bit.

Demo code:

  $ cat <<EOF >test.c
  #include <sys/stat.h>
  int main()
  {
      struct stat buf;
      return stat("/", &buf);
  }
  EOF

32-bit build:

  $ nix-shell -p gcc --argstr system i686-linux

  nix-shell$ gcc test.c && ltrace ./a.out
  __libc_start_main([ "./a.out" ] <unfinished ...>
  stat(0x804a008, 0xffa4b644, 895, 0) = 0
  +++ exited (status 0) +++

  nix-shell$ gcc -D_FILE_OFFSET_BITS=64 test.c && ltrace ./a.out
  __libc_start_main([ "./a.out" ] <unfinished ...>
  stat64(0x804a008, 0xffdcf61c, 100, 0xffdcfaeb) = 0
  +++ exited (status 0) +++

  nix-shell$ file a.out
  a.out: ELF 32-bit LSB executable, Intel 80386, [...]

64-bit build:

  $ nix-shell -p gcc

  nix-shell$ gcc test.c && ltrace ./a.out
  stat(0x402004, 0x7ffc50a9d740, 0x7ffc50a9d908, 0x403db0) = 0
  +++ exited (status 0) +++

  nix-shell$ gcc -D_FILE_OFFSET_BITS=64 test.c && ltrace ./a.out
  stat64(0x402004, 0x7ffd5cbafba0, 0x7ffd5cbafd68, 0x403db0) = 0
  +++ exited (status 0) +++

  nix-shell$ file a.out
  a.out: ELF 64-bit LSB executable, x86-64, [...]
2025-07-09 17:32:54 +02:00
Wolfgang Hommel
6404d81f63 Merge pull request #504 from bjornfor/system-v-semaphores
Add optional System V semaphores
2025-06-27 19:33:14 +02:00
Bjørn Forsman
44c578c6d6 Add optional System V semaphore backend
By building with -DFT_SEMAPHORE_BACKEND=FT_SYSV, the System V semaphore
API is unsed instead of POSIX. This works around a glibc bug[1] and
fixes https://github.com/wolfcw/libfaketime/issues/427
("libfaketime hangs forever when 32-bit process is executed within 64-bit process").
The default backend is still POSIX, in case there are regressions in the
new code.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=17980

Ref https://github.com/wolfcw/libfaketime/issues/427
2025-06-23 21:00:21 +02:00
Bjørn Forsman
2649cdb156 Add semaphore abstraction layer
Add ft_sem_*() functions that use the POSIX semaphore API.

In preparation for adding System V semaphores as an alternative to POSIX
semaphores, because glibc breaks POSIX semaphores when operating in
mixed 32- and 64-bit environments[1].

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=17980
2025-06-23 16:42:49 +02:00
Bjørn Forsman
71b31e908d Add missing newline to error message 2025-06-23 16:19:26 +02:00
Wolfgang Hommel
523584abd4 Merge pull request #503 from sammytranGeo/fix-monotonic-timedwait
Fix MONOTONIC pthread_cond_timedwait when REALTIME is set
2025-06-14 13:04:11 +02:00
Sammy Tran
a2e406c669 Fix MONOTONIC pthread_cond_timedwait when REALTIME is set 2025-06-13 17:13:43 -04:00
Wolfgang Hommel
3ccdd344aa Preparations for v0.9.12 release 2025-06-09 14:31:15 +02:00
Wolfgang Hommel
f63569e422 Merge pull request #502 from usertam/patch/fix-fake-stat64buf-on-linux
Fix fake_stat64buf() again
2025-06-09 14:26:48 +02:00
usertam
d276658b74 libfaketime.c: fix fake_stat64buf() again
I got the logic wrong in PR #501 in the inner `#ifndef __APPLE__`.
This broke building on linux. This should fix it.
2025-06-09 19:19:37 +08:00
Wolfgang Hommel
cb48e454be Merge pull request #501 from usertam/patch/clean-up-and-readme
Fix compiler warnings and update README.OSX
2025-06-09 10:24:59 +02:00
usertam
77ae25f529 README.OSX: document about the new arm64e ABI 2025-06-08 23:23:50 +08:00
usertam
30d7defcf5 libfaketime.c: get rid of stat64 things on aarch64-darwin
To give more context, stat64 is a child of large-file support (LFS)
back in 1996, during the transition from 32-bit to 64-bit. People
wanted 64-bit inodes in 32-bit systems, hence stat and stat64.

Nowadays where everything is 64-bit, stat64 is mostly just an alias
to stat, as stat is already native 64-bit. On modern implementations
like musl, stat64 is even dropped entirely as a sane default. We
observe the same in darwin's stat.h:

  #if !__DARWIN_ONLY_64_BIT_INO_T
  struct stat64 __DARWIN_STRUCT_STAT64;
  #endif /* !__DARWIN_ONLY_64_BIT_INO_T */

Because struct stat64 doesn't ever exist on aarch64-darwin, and we
don't have to worry about people using stat64 calls, we can safely
remove all stat64 bloat, according to __DARWIN_ONLY_64_BIT_INO_T.

I nuked fake_stat64buf because only STAT64_HANDLER is using it, and
only non-darwin stat64 things use that handler. I didn't do more
because people might still use stat64 things on x86_64 (on glibc)
and other older 32-bit platforms, and we still need to hook those.

A loose follow up to PR #453. Fixes the remaining clang warnings on
aarch64-darwin.
2025-06-08 19:34:48 +08:00
Wolfgang Hommel
9f7b304dbe Merge pull request #500 from usertam/patch/fix-darwin-and-arm64e
Fix darwin and arm64e
2025-06-07 23:03:01 +02:00
usertam
0277016bb5 Makefile.OSX: add -fptrauth-* flags for arm64e to work properly
Particularly we need -fptrauth-calls, so when pthread_once indirectly
calls ftpl_really_init, it won't fail PAC.
2025-06-07 20:51:07 +08:00
usertam
0e2dbe4ae1 libfaketime.c: correct macro from __APPLEOSX__ to __APPLE__
This fixes the recursive pthread_once deadlock on darwin platforms.
It looks something like this:

  Trace/BPT trap: 5

  BUG IN CLIENT OF LIBPLATFORM: Trying to recursively lock an os_once_t

The macro __APPLEOSX__ is never defined, instead __APPLE__ should be used.
This mistake inadvertently caused system_time_from_system() to always take
the linux code path on darwin, leading to recursive calls during ftpl_init().

This was exposed by PR #488 which removed the ad-hoc recursion detection
that previously masked this issue.
2025-06-07 20:51:06 +08:00
Wolfgang Hommel
45d29c8256 Merge pull request #497 from usertam/patch/compile-both-arm64-arm64e
Compile for arm64 on darwin again
2025-06-03 21:48:40 +02:00
usertam
264e8efad7 Makefile.OSX: compile a fat library of both arm64e and arm64 2025-06-01 22:50:46 +08:00
usertam
3a3d1deebc Revert "Check if the user is on ARM64, add target to CFLAGS/LDFLAGS"
This reverts commit 2a2af0fcdc.
2025-06-01 01:03:58 +08:00
Wolfgang Hommel
6714b98794 Preparations for v0.9.11 release 2025-05-25 10:00:14 +02:00
Wolfgang Hommel
3e56ada3ff Merge pull request #495 from PiotrBzdrega/master
missing FUTEX_CLOCK_REALTIME declaration when build with flags -DINTERCEPT_SYSCALL -DINTERCEPT_FUTEX
2025-05-24 13:03:01 +02:00
PiotrBzdrega
2dca058f5c missing FUTEX_CLOCK_REALTIME declaration when build with flags -DINTERCEPT_SYSCALL -DINTERCEPT_FUTEX 2025-05-13 15:01:47 +02:00
Wolfgang Hommel
2e2d3eefb5 Merge pull request #493 from totoroyyb/master
[DRAFT] fix: unhandled futex-related syscall
2025-03-29 11:37:26 +01:00
Yibo Yan
fa731ed50f fix: unhandled futex wait syscall 2025-03-26 21:02:47 +00:00
Wolfgang Hommel
3f6467d421 Test different Ubuntu versions 2025-01-29 17:36:42 +01:00
Wolfgang Hommel
2dac72caba Test different Ubuntu versions 2025-01-29 17:21:51 +01:00
Wolfgang Hommel
21af5175f5 pthread.h on macOS 2025-01-28 21:30:17 +01:00
Wolfgang Hommel
b5a48c870b Merge pull request #488 from ijackson/races
Fix several data races
2025-01-28 19:59:24 +01:00
Wolfgang Hommel
52fe3cc442 Merge pull request #487 from ijackson/t64
Fake 64-bit time on 32-bit systems
2025-01-28 06:26:19 +01:00
Wolfgang Hommel
63aef51102 Merge pull request #486 from ijackson/utime
Re-disable faking utime by default
2025-01-28 06:24:32 +01:00
Ian Jackson
50e2c56914 Don't use _try_ locking calls for monotonic_conds_lock
This reverts commit 8ef74e33b6
   "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 8ef74e33b6 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.
2025-01-27 13:12:45 +00:00
Ian Jackson
b6e87c6f26 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.
2025-01-27 13:12:45 +00:00
Ian Jackson
d9ba684b18 Replace data race with use of pthread_once (ft_shm_init) 2025-01-27 13:12:45 +00:00
Ian Jackson
2503b0fffc Replace data race with use of pthread_once (ftpl_init)
At the cost of no longer nicely detecting recursive initialisation
problems.

Fixes Debian bug #1093599
2025-01-27 13:12:38 +00:00
Ian Jackson
97721e5491 Interpose gettimeofday64 2025-01-27 12:27:03 +00:00
Ian Jackson
fdb5ba3f7a Interpose __time64 2025-01-27 12:27:03 +00:00
Ian Jackson
f289bf702f 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$
2025-01-27 12:27:03 +00:00
Helge Deller
536889d797 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.

Patch originally supplied here
  https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1064555
2025-01-27 12:26:40 +00:00
Ian Jackson
19b2476534 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.
2025-01-27 12:22:48 +00:00
Wolfgang Hommel
92c322507c Merge pull request #485 from LocutusOfBorg/master
test/libmallocintercept.c: fix write function unused return value
2025-01-25 13:04:34 +01:00
Gianfranco Costamagna
0516055224 test/libmallocintercept.c: fix write function unused return value
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));
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~
2025-01-22 12:22:37 +01:00
Wolfgang Hommel
ba9ed5b289 Merge pull request #473 from EgnalZurc/patch-1
Preventing shared semaphore to be used again
2024-06-05 19:52:44 +02:00
Egnal Zurc
7e9d69b98f Preventing shared sem to be used again
The shared semaphore is closed but it's not assigned to null.
That's required because the logic check the semaphore status if it's not null. For this reason, we are getting a core some times.
2024-06-05 12:16:41 +02:00
Wolfgang Hommel
a04750217b ft_dlvsym() check for NULL version 2024-03-24 21:08:36 +01:00
Wolfgang Hommel
a3e91605ad Merge pull request #463 from Rob--W/issue-130-dlsym
Add FAKETIME_IGNORE_SYMBOLS to skip unneeded dlsym
2024-03-19 19:43:22 +01:00
Wolfgang Hommel
b716122cbe Merge pull request #465 from Rob--W/add-disable-shm-option
Add --disable-shm / FAKETIME_DISABLE_SHM
2024-03-19 19:15:38 +01:00
Wolfgang Hommel
23bec3882d Merge pull request #466 from joshuataylor/feature/macos-arm64
Check if the user is on ARM64, add target to CFLAGS/LDFLAGS
2024-03-18 19:26:16 +01:00
Josh Taylor
2a2af0fcdc Check if the user is on ARM64, add target to CFLAGS/LDFLAGS 2024-03-18 13:48:26 +08:00
Rob Wu
39fdbde365 Add --disable-shm / FAKETIME_DISABLE_SHM
The use of shared memory has side effects. Currently, the only way to
opt out of shared memory is by compiling with -DFAKE_STATELESS.

To allow disabling shared memory without recompiling, this patch
introduces the --disable-shm option to `faketime`, equivalent to
setting the `FAKETIME_DISABLE_SHM=1` environment variable.
2024-03-13 00:02:08 +01:00
Rob Wu
c745ab783b Add FAKETIME_IGNORE_SYMBOLS to skip unneeded dlsym 2024-03-12 02:40:56 +01:00
Wolfgang Hommel
f32986867a Merge pull request #453 from martinetd/musl
fix build on recent musl (stat64 compat)
2024-01-18 06:22:33 +01:00
Dominique Martinet
b2fe742aa7 fix build on recent musl (stat64 compat)
musl removed LFS64 compat[1] so stat64 is no longer defined by default,
but we can bring it back for now through _LARGEFILE64_SOURCE

Link: https://www.openwall.com/lists/musl/2022/09/26/1 [1]
Fixes: #446
2024-01-18 12:47:41 +09:00
Wolfgang Hommel
265651969b Merge pull request #451 from RCoeurjoly/master
sycall also watches the timestamp_file
2024-01-02 15:14:05 +01:00
Roland Coeurjoly
6a0f35dcbd sycall also watches the timestamp_file 2024-01-02 15:09:12 +01:00
Wolfgang Hommel
0af80dd593 Merge pull request #435 from kraj/master
Makefile: Detect compiler in makefile
2023-08-25 11:50:32 +02:00
Khem Raj
8908752a25 Makefile: Detect compiler in makefile
Add compiler specific options based on detected compiler gcc/clang
2023-08-24 10:09:53 -07:00
Wolfgang Hommel
27b9c83a27 Merge pull request #434 from sliquister/master
adding support for faking statx
2023-08-01 07:35:14 +02:00
Valentin Gatien-Baron
942b30e940 adding support for faking statx 2023-07-30 20:55:48 -04:00
Wolfgang Hommel
7154a3f42c Set FAKETIME_FLSHM=1 to auto-unset FAKETIME_SHARED (addresses #427) 2023-06-08 13:12:39 +02:00
Wolfgang Hommel
0c2e3d41be the missing else branch on CLOCK_MONOTONIC in clock_nanosleep (#426) 2023-06-06 20:10:31 +02:00
Wolfgang Hommel
f262b5fba7 Re-check fake_monotonic_setting in clock_nanosleep (#426) 2023-06-06 19:48:18 +02:00
Wolfgang Hommel
d17bb114c6 Honor fake_monotoic_clock setting in clock_nanosleep, addresses #426 2023-06-04 13:21:09 +02:00
Wolfgang Hommel
7df1bf7122 Fix #424 2023-04-30 20:26:07 +02:00
Wolfgang Hommel
6d072025c0 Merge pull request #422 from fixindan/dead_lock_no_return
Swapped out pthread_rwlock_xxlock(), which doesn't return if it can't…
2023-02-25 12:58:03 +01:00
Dixin Fan
8ef74e33b6 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. 2023-02-24 16:18:47 -06:00
Wolfgang Hommel
6fc4ae74f4 Merge pull request #416 from sliquister/master
ensure faketime can't be initialized more than once
2023-01-27 20:43:41 +01:00
Valentin Gatien-Baron
1997652d8e ensure faketime can't be initialized more than once
One callsite of ftpl_init wasn't protected by the "if (!initialized)"
condition, specifically:

static void ftpl_init (void) __attribute__ ((constructor));

If another "constructor" was called before this one, and that other
constructor used time or filesystem functions, ftlp_init would be
initialized by that other constructor, and then reinitialized by the
ftpl_init constructor. At that point, confusion ensues.
2023-01-16 21:26:39 -05:00
Wolfgang Hommel
de37190d40 Merge pull request #415 from usertam/master
libfaketime.c: wrap timespec_get in TIME_UTC macro
2022-12-20 19:25:59 +01:00
Samuel Tam
e0e6b79568 libfaketime.c: wrap timespec_get in TIME_UTC macro
Function `timespec_get` is not guaranteed to be declared in MacOS
since its standard library is non-conformance to modern standards.
Therefore, skip patching `timespec_get` if it is undeclared by the
standard library.

The detection of `timespec_get` is based on the conjecture that the
macro `TIME_UTC` is only defined when `timespec_get` is declared.
2022-12-20 02:08:45 +08:00
Wolfgang Hommel
df8a045597 Fix for Debian Bug#1017865 as provided by Samuel Thibault 2022-08-28 13:39:18 +02:00
Wolfgang Hommel
2adb56b07f Merge pull request #408 from daglem/short_read
Handle short reads from timestamp file
2022-08-28 13:33:51 +02:00
Dag Lem
1c80b19fe5 Handle short reads from timestamp file 2022-08-22 11:21:14 +02:00
Wolfgang Hommel
32eedc2b42 Merge pull request #406 from enr0n/master
test/snippets: fix time.c compiler error on 32-bit arches
2022-08-10 22:27:39 +02:00
Nick Rosbrook
ccc9992840 test/snippets: fix time.c compiler error on 32-bit arches
Cast t to unsigned long and use the %lu format specifier instead of %zd.
This is more portable to 32-bit arches, avoiding the following compiler
error:

 snippets/time.c:2:31: error: format ‘%zd’ expects argument of type ‘signed size_t’, but argument 3 has type ‘time_t’ {aka ‘long int’} [-Werror=format=]
     2 | printf("[%s] time() yielded %zd\n", where, t);
       |                             ~~^            ~
       |                               |            |
       |                               int          time_t {aka long int}
       |                             %ld
 cc1: all warnings being treated as errors
2022-08-10 14:40:42 -04:00
Wolfgang Hommel
a059f1294f Merge pull request #404 from perldude/perldude-issue-403
issue#403
2022-07-25 20:32:23 +02:00
Wolfgang Hommel
f4bf28356c Merge pull request #397 from j-xella/sun_compiler
Refactor to get rid of some non-standard gcc extensions
2022-07-25 20:32:02 +02:00
Michael Sullivan
5c63238544 issue#403
Disable including `sys/time.h` on ARM to prevent conflicting declarations of `gettimeofday()`.
2022-07-25 09:53:26 -07:00
Wolfgang Hommel
be4e373e63 Merge pull request #400 from sveyret/shared-mem-sync
Reset shared memory when start time is reset
2022-06-20 19:41:24 +02:00
Stéphane Veyret
431f09eb19 Reset shared memory when start time is reset 2022-06-17 16:38:41 +02:00
Aleksandr Jakusev
326c20ebb5 Refactor to get rid of some non-standard gcc extensions
Without the changes Sun studio 12.8 compiler fails, for example

Note that only the errors are fixed. On the compiler above, some
warnings still remain, so -Werror has to be removed as well from the
compiler switches in order for the compilation to succeed.
2022-05-26 23:44:39 +00:00
Wolfgang Hommel
b61fade280 honor dont_fake_monotonic in experimental sem_clockwait() (addresses #390) 2022-05-16 19:20:33 +02:00
Wolfgang Hommel
859751e2cb fix reverse user_rate in sem_clockwait() (addresses #390) 2022-05-14 23:09:03 +02:00
Wolfgang Hommel
f706373bc2 Experimental sem_clockwait() support (addresses #390) 2022-05-11 21:47:35 +02:00
Wolfgang Hommel
e7ca8378ca Merge pull request #391 from psychon/asan2
Fix another hang under ASAN
2022-05-09 19:39:43 +02:00
Uli Schlachter
3b3f80a42f Fix another hang under ASAN
We have a long-running program that we want to run under sanitizers for
extra error checking and under faketime to speed up the clock. This
program hangs after a while. Backtraces suggest that the hangs occur
because of recursion in the memory allocator, which apparently locks a
non-recursive mutex.

Specifically, what we see is that due to our use of FAKETIME_NO_CACHE=1,
libfaketime wants to reload the config file inside a (random) call to
clock_gettime(). libfaketime then uses fopen() / fclose() for reading
the config files. These function allocate / free a buffer for reading
data and specifically the call to free() that happens inside fclose()
ends up calling clock_gettime() again. At this point, libfaketime locks
up because it has a time_mutex that is locked and none-recursive.

Sadly, I did not manage to come up with a stand-alone reproducer for
this problem. Also, the above description is from memory after half a
week of vacation, so it might be (partially) wrong.

More information can be found here:

- https://github.com/wolfcw/libfaketime/issues/365#issuecomment-1115802530
- https://github.com/wolfcw/libfaketime/issues/365#issuecomment-1116178907

This commit first adds a test case. This new test uses the already
existing libmallocintercept.so to cause calls to clock_gettime() during
memory allocation routines. The difference to the already existing
version is that additionally FAKETIME_NO_CACHE and
FAKETIME_TIMESTAMP_FILE are set. This new test hung with its last output
suggesting a recursive call to malloc:

Called malloc() from libmallocintercept...successfully
Called free() on from libmallocintercept...successfully
Called malloc() from libmallocintercept...Called malloc() from libmallocintercept...

Sadly, gdb was unable to provide a meaningful backtrace for this hang.

Next, I replaced the use of fopen()/fgets()/fgets() with
open()/read()/close(). This code no longer reads the config file
line-by-line, but instead it reads all of it at once and then "filters
out" the result (ignores comment lines, removes end of line markers).

I tried to keep the behaviour of the code the same, but I know at least
one difference: Previously, the config file was read line-by-line and
lines that began with a comment character were immediately ignored. The
new code only reads the config once and then removes comment lines.
Since the buffer that is used contains only 256 characters, it is
possible that config files that were previously parsed correctly now
longer parse. A specific example: if a file begins with 500 '#'
characters in its first line and then a timestamp in the second line,
the old code was able to parse this file while the new code would only
see an empty file.

After this change, the new test no longer hangs. Sadly, I do not
actually know its effect on the "actual bug" that I wanted to fix, but
since there are no longer any calls to fclose(), there cannot be any
hangs inside fclose().

Signed-off-by: Uli Schlachter <psychon@znc.in>
2022-05-09 13:53:51 +02:00
Wolfgang Hommel
2bfbe19f71 silence minor type warning in libmallocintercept.c 2022-05-08 21:24:51 +02:00
Wolfgang Hommel
75cbe8e507 silence minor type warning in libmallocintercept.c 2022-05-08 21:20:29 +02:00
Wolfgang Hommel
e8838709ea silence minor type warning in libmallocintercept.c 2022-05-08 21:09:45 +02:00
Wolfgang Hommel
bf3a08b04d silence minor type warning in libmallocintercept.c 2022-05-08 21:05:10 +02:00
Wolfgang Hommel
141d1a7a87 Merge pull request #389 from psychon/asan
Work-around / fix libasan incompatibility
2022-05-08 20:56:16 +02:00
Uli Schlachter
0f79f21e11 Add libmallocintercept.so to make clean
Signed-off-by: Uli Schlachter <psychon@znc.in>
2022-05-08 19:20:51 +02:00
Uli Schlachter
450d5d4549 Disable FAILRE_PRE_INIT_CALLS by default
Signed-off-by: Uli Schlachter <psychon@znc.in>
2022-05-08 19:17:27 +02:00
Uli Schlachter
fff49b23fc Add FAIL_PRE_INIT_CALLS define
This commit adds a new define FAIL_PRE_INIT_CALLS. When that define is
set, calls to clock_gettime() that occur before ftpl_init() was called
(due to being marked with __attribute__((constructor))) will just fail
and return -1.

After this commit, the test case added in the previous commit no longer
hangs. To make this actually work, this new define is enabled by
default.

Fixes: https://github.com/wolfcw/libfaketime/issues/365
Signed-off-by: Uli Schlachter <psychon@znc.in>
2022-05-04 14:51:35 +02:00
Uli Schlachter
6e0f978079 Add test reproducing ASAN-like hangs
Backtraces suggest that AddressSanitizer replaces malloc() with a
function that

- locks a mutex and
- calls clock_gettime() while the mutex is held

This commit adds a test that implements a trivial malloc() that behaves
similarly. Currently, this test hangs.

Signed-off-by: Uli Schlachter <psychon@znc.in>
2022-05-04 14:25:13 +02:00
Wolfgang Hommel
f836ea3eb3 Merge pull request #388 from dkg/cleanup-random-tests
test: remember to clean up repeat_random
2022-04-16 21:17:55 +02:00
Wolfgang Hommel
642b6ee870 Merge pull request #387 from dkg/clean-syscall-tests
clean up tests related to syscall
2022-04-16 21:17:41 +02:00
Daniel Kahn Gillmor
03cb104691 test: remember to clean up repeat_random 2022-04-16 10:27:53 -07:00
Daniel Kahn Gillmor
7e86fb5419 tests: avoid testing syscall snippets if -DINTERCEPT_SYSCALL is not set
See https://bugs.debian.org/1007828
2022-04-16 10:06:23 -07:00
Daniel Kahn Gillmor
e9c74131fc tests: clean whitespace in Makefile 2022-04-16 10:05:40 -07:00
Wolfgang Hommel
8fa0530d83 Honor tv_nsec in timeouts on ppoll() calls (addresses #381) 2022-04-02 13:52:18 +02:00
Wolfgang Hommel
98e3d3f36f select(): Scale timeout parameter by user rate on return (addresses #382) 2022-04-02 13:47:04 +02:00
Wolfgang Hommel
0ca35dd8c4 Merge pull request #375 from inorton/fix_374_UFAKE_STAT
fixes #374 fix compiling without FAKE_STAT or with FAKE_UTIME
2022-03-18 19:31:35 +01:00
Ian Norton
2d941a894f fixes #374 fix compiling without FAKE_STAT 2022-03-18 12:25:55 +00:00
Wolfgang Hommel
f50664f0bd Update NEWS file about v0.9.10 changes 2022-03-04 20:33:18 +01:00
27 changed files with 2226 additions and 436 deletions

View File

@@ -12,7 +12,7 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
os: [ubuntu-latest, ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:

19
NEWS
View File

@@ -1,4 +1,23 @@
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
- improved macOS Monterey support through dyld interposing
- changed interception hooks for stat() and similar functions,
refactored to use a common handler (@sirainen)

22
README
View File

@@ -1,5 +1,5 @@
libfaketime, version 0.9.10 (March 2022)
========================================
libfaketime, version 0.9.12 (June 2025)
=======================================
Content of this file:
@@ -256,10 +256,10 @@ the difference:
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
FAKETIME_DONT_RESET=1 \
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
In the second example, the "date" command will always print the same time,
while in the first example, with FAKETIME_DONT_RESET set, time will increment
@@ -354,7 +354,7 @@ like "+0 x2", i.e., use an explicit zero offset as a prefix in your FAKETIME.
For testing, your should run a command like
LD_PRELOAD=./libfaketime.so.1 FAKETIME="+1,5y x10,0" \
/bin/bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
For each second that the endless loop sleeps, the executed bash shell will
think that 10 seconds have passed ($SECONDS is a bash-internal variable
@@ -420,6 +420,15 @@ LD_PRELOAD=/path/to/libfaketime.so.1 \
# (in a different terminal window or whatever)
touch -t 2002290123.45 /tmp/my-demo-file.tmp
Setting the environment variable FAKETIME_FOLLOW_ABSOLUTE=1 enables a submode
of FAKETIME_FOLLOW_FILE behavior where fake time ONLY advances when the follow
file's timestamp advances. In this mode an application that is not subject to
libfaketime LD_PRELOAD intercept can absolutely control time for applications
that are hooked by libfaketime. For example, a host application can control the
timestamp of a follow file mapped into a container to implement (relatively)
clean pause/resume behavior for fake time applications running within the
container.
Changing the 'x' modifier during run-time
-----------------------------------------
@@ -486,6 +495,9 @@ for long-running systems (servers with high uptime) and systems on which
a lot of processes are started (e.g., servers handling many containers
or similar virtualization mechanisms).
Use of shared memory can be disabled by setting the FAKETIME_DISABLE_SHM
environment variable, or equivalently, passing --disable-shm to faketime.
Intercepting time-setting calls
-------------------------------

View File

@@ -166,3 +166,50 @@ The environment variable FAKETIME can be changed at application run-time
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.

View File

@@ -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
@@ -32,16 +32,19 @@ use the advanced timestamp specification format.
\fB\--exclude-monotonic\fR
Do not fake time when the program makes a call to clock_gettime with a CLOCK_MONOTONIC clock.
.TP
\fB\--disable-shm\fR
Disable use of shared memory by libfaketime.
.TP
\fB\--date-prog <PATH>\fR
Use a specific GNU-date compatible implementation of the helper used to transform "timestamp format" strings into programmatically usable dates, instead of a compile-time default guess for the generic target platform.
.SH EXAMPLES
.nf
faketime 'last Friday 5 pm' /bin/date
faketime '2008-12-24 08:15:42' /bin/date
faketime -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
faketime -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
faketime -f '+2,5y i2,0' /bin/bash -c 'while true; do date ; sleep 1 ; done'
faketime 'last Friday 5 pm' date
faketime '2008-12-24 08:15:42' date
faketime -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
faketime -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
faketime -f '+2,5y i2,0' bash -c 'while true; do date ; sleep 1 ; done'
In this single case all spawned processes will use the same global clock without restarting it at the start of each process.
(Please note that it depends on your locale settings whether . or , has to be used for fractional offsets)

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
@@ -84,6 +95,11 @@
# - avoid that the faketime wrapper complains when running within a
# libfaketime environment
#
# FAIL_PRE_INIT_CALLS
# - If the time is queried before the library was initialised, let the
# call fail instead of trying to initialise on-the-fly. This fixes /
# works around hangs that were seen with address sanitizer.
#
# * Compilation addition: second libMT target added for building the pthread-
# enabled library as a separate library
#
@@ -110,20 +126,55 @@ PREFIX ?= /usr/local
LIBDIRNAME ?= /lib/faketime
PLATFORM ?=$(shell uname)
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -Wno-nonnull-compare -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"' $(FAKETIME_COMPILE_CFLAGS)
ifneq ($(filter android%,$(subst -, ,$(CC))),)
IS_ANDROID := true
endif
ifeq ($(shell $(CC) -dM -E - < /dev/null | grep -c "__ANDROID__"),1)
IS_ANDROID := true
endif
ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1)
COMPILER := clang
else
COMPILER := gcc
endif
export COMPILER
CFLAGS += -std=gnu99 -Wall -Wextra -Werror -DFAKE_PTHREAD -DFAKE_STAT -DFAKE_UTIME -DFAKE_SLEEP -DFAKE_TIMERS -DFAKE_INTERNAL_CALLS -fPIC -DPREFIX='"'$(PREFIX)'"' -DLIBDIRNAME='"'$(LIBDIRNAME)'"' $(FAKETIME_COMPILE_CFLAGS)
ifeq ($(COMPILER),clang)
CFLAGS += -Wno-tautological-pointer-compare
endif
ifeq ($(COMPILER),gcc)
CFLAGS += -Wno-nonnull-compare
endif
ifeq ($(PLATFORM),SunOS)
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
endif
LIB_LDFLAGS += -shared
LDFLAGS += $(FAKETIME_LINK_FLAGS) -lpthread
ifneq ($(PLATFORM),SunOS)
LDFLAGS += -Wl,--version-script=libfaketime.map
ifdef IS_ANDROID
CFLAGS += -D__ANDROID__
endif
LDADD += -ldl -lm -lrt
BIN_LDFLAGS += -lrt
LIB_LDFLAGS += -shared
LDFLAGS += $(FAKETIME_LINK_FLAGS)
ifneq ($(PLATFORM),SunOS)
ifndef IS_ANDROID
LDFLAGS += -Wl,--version-script=libfaketime.map
endif
endif
ifdef IS_ANDROID
LDADD += -ldl -lm
BIN_LDFLAGS +=
else
LDADD += -ldl -lm -lrt -lpthread
BIN_LDFLAGS += -lrt -lpthread
endif
SRC = libfaketime.c
LIBS_OBJ = libfaketime.o libfaketimeMT.o
@@ -136,17 +187,20 @@ all: ${LIBS} ${BINS}
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
${LIBS_OBJ}: libfaketime.c
ft_sem.o: ft_sem.c ft_sem.h
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} $<
${LIBS_OBJ}: libfaketime.c ft_sem.h
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $<
%.so.${SONAME}: %.o libfaketime.map
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}
%.so.${SONAME}: %.o ft_sem.o libfaketime.map
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ft_sem.o ${LDADD}
${BINS}: faketime.c
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
${BINS}: faketime.c ft_sem.o ft_sem.h
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ft_sem.o ${LDFLAGS} ${BIN_LDFLAGS}
clean:
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS} ft_sem.o
distclean: clean
@echo

View File

@@ -56,7 +56,20 @@ INSTALL ?= install
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
# 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)
SONAME = 1
LIBS = libfaketime.${SONAME}.dylib
@@ -64,14 +77,26 @@ BINS = faketime
all: ${LIBS} ${BINS}
ifeq ($(ARCH),arm64)
libfaketime.${SONAME}.dylib: libfaketime.c
${CC} -o libfaketime.arm64e.dylib ${CFLAGS} -arch arm64e -fptrauth-calls -fptrauth-returns ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
${CC} -o libfaketime.arm64.dylib ${CFLAGS} -arch arm64 ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
lipo -create -output $@ libfaketime.arm64e.dylib libfaketime.arm64.dylib
rm libfaketime.arm64e.dylib libfaketime.arm64.dylib
faketime: faketime.c
${CC} -o $@ ${CFLAGS} -arch arm64e -arch arm64 ${LDFLAGS} $<
else
libfaketime.${SONAME}.dylib: libfaketime.c
${CC} -o $@ ${CFLAGS} ${LDFLAGS} ${LIB_LDFLAGS} -install_name ${PREFIX}/lib/faketime/$@ $<
faketime: faketime.c
${CC} -o $@ ${CFLAGS} ${LDFLAGS} $<
endif
clean:
@rm -f ${OBJ} ${LIBS} ${BINS}
@rm -f ${OBJ} ${LIBS} ${BINS} libfaketime.arm64e.dylib libfaketime.arm64.dylib
distclean: clean
@echo

51
src/android_compat.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef ANDROID_COMPAT_H
#define ANDROID_COMPAT_H
#ifdef __ANDROID__
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
static inline const char *__android_shm_tmpdir(void)
{
const char *dir = getenv("FAKETIME_SHM_DIR");
if (dir == NULL)
dir = getenv("TMPDIR");
if (dir == NULL)
dir = "/data/local/tmp";
return dir;
}
static inline int __android_shm_open(const char *name, int oflag, mode_t mode)
{
char path[512];
const char *dir = __android_shm_tmpdir();
while (*name == '/') name++;
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
int fd = open(path, oflag, mode);
if (fd == -1 && (oflag & O_CREAT))
{
mkdir(dir, 0777);
fd = open(path, oflag, mode);
}
return fd;
}
static inline int __android_shm_unlink(const char *name)
{
char path[512];
const char *dir = __android_shm_tmpdir();
while (*name == '/') name++;
snprintf(path, sizeof(path), "%s/faketime_shm_%s", dir, name);
return unlink(path);
}
#define shm_open __android_shm_open
#define shm_unlink __android_shm_unlink
#endif /* __ANDROID__ */
#endif /* ANDROID_COMPAT_H */

View File

@@ -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
@@ -44,11 +44,12 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <semaphore.h>
#include "ft_sem.h"
#include "android_compat.h"
#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";
@@ -60,6 +61,7 @@ static const char *date_cmd = "date";
/* semaphore and shared memory names */
char sem_name[PATH_BUFSIZE] = {0}, shm_name[PATH_BUFSIZE] = {0};
static ft_sem_t wrapper_sem;
void usage(const char *name)
{
@@ -77,15 +79,18 @@ void usage(const char *name)
" --exclude-monotonic : Prevent monotonic clock from drifting (not the raw monotonic one)\n"
#ifdef FAKE_PID
" -p PID : Pretend that the program's process ID is PID\n"
#endif
#ifndef FAKE_STATELESS
" --disable-shm : Disable use of shared memory by libfaketime.\n"
#endif
" --date-prog PROG : Use specified GNU-compatible implementation of 'date' program\n"
"\n"
"Examples:\n"
"%s 'last friday 5 pm' /bin/date\n"
"%s '2008-12-24 08:15:42' /bin/date\n"
"%s -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
"%s -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
"%s -f '+2,5y i2,0' /bin/bash -c 'date; while true; do date; sleep 1 ; done'\n"
"%s 'last friday 5 pm' date\n"
"%s '2008-12-24 08:15:42' date\n"
"%s -f '+2,5y x10,0' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
"%s -f '+2,5y x0,50' bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'\n"
"%s -f '+2,5y i2,0' bash -c 'date; while true; do date; sleep 1 ; done'\n"
"In this single case all spawned processes will use the same global clock\n"
"without restarting it at the start of each process.\n\n"
"(Please note that it depends on your locale settings whether . or , has to be used for fractions)\n"
@@ -95,9 +100,9 @@ void usage(const char *name)
/** Clean up shared objects */
static void cleanup_shobjs()
{
if (-1 == sem_unlink(sem_name))
if (-1 == ft_sem_unlink(&wrapper_sem))
{
perror("faketime: sem_unlink");
perror("faketime: ft_sem_unlink");
}
if (-1 == shm_unlink(shm_name))
{
@@ -150,6 +155,14 @@ int main (int argc, char **argv)
curr_opt++;
continue;
}
#ifndef FAKE_STATELESS
else if (0 == strcmp(argv[curr_opt], "--disable-shm"))
{
setenv("FAKETIME_DISABLE_SHM", "1", true);
curr_opt++;
continue;
}
#endif
else if (0 == strcmp(argv[curr_opt], "--date-prog"))
{
curr_opt++;
@@ -244,9 +257,8 @@ int main (int argc, char **argv)
curr_opt++;
{
/* create semaphores and shared memory */
/* create lock and shared memory */
int shm_fd;
sem_t *sem;
struct ft_shared_s *ft_shared;
char shared_objs[PATH_BUFSIZE * 2 + 1];
@@ -260,10 +272,10 @@ int main (int argc, char **argv)
snprintf(sem_name, PATH_BUFSIZE -1 ,"/faketime_sem_%ld", (long)getpid());
snprintf(shm_name, PATH_BUFSIZE -1 ,"/faketime_shm_%ld", (long)getpid());
if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
if (-1 == ft_sem_create(sem_name, &wrapper_sem))
{
perror("faketime: sem_open");
fprintf(stderr, "The faketime wrapper only works on platforms that support the sem_open()\nsystem call. However, you may LD_PRELOAD libfaketime without using this wrapper.\n");
perror("faketime: ft_sem_create");
fprintf(stderr, "The faketime wrapper failed to create its lock.\nHowever, you may LD_PRELOAD libfaketime without using this wrapper.\n");
exit(EXIT_FAILURE);
}
@@ -271,15 +283,12 @@ int main (int argc, char **argv)
if (-1 == (shm_fd = shm_open(shm_name, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR)))
{
perror("faketime: shm_open");
if (-1 == sem_unlink(argv[2]))
{
perror("faketime: sem_unlink");
}
ft_sem_unlink(&wrapper_sem);
exit(EXIT_FAILURE);
}
/* set shm size */
if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s)))
{
perror("faketime: ftruncate");
cleanup_shobjs();
@@ -295,9 +304,9 @@ int main (int argc, char **argv)
exit(EXIT_FAILURE);
}
if (sem_wait(sem) == -1)
if (ft_sem_lock(&wrapper_sem) == -1)
{
perror("faketime: sem_wait");
perror("faketime: ft_sem_lock");
cleanup_shobjs();
exit(EXIT_FAILURE);
}
@@ -305,12 +314,16 @@ int main (int argc, char **argv)
/* init elapsed time ticks to zero */
ft_shared->ticks = 0;
ft_shared->file_idx = 0;
ft_shared->start_time.real.tv_sec = 0;
ft_shared->start_time.real.tv_nsec = -1;
ft_shared->start_time.mon.tv_sec = 0;
ft_shared->start_time.mon.tv_nsec = -1;
ft_shared->start_time.mon_raw.tv_sec = 0;
ft_shared->start_time.mon_raw.tv_nsec = -1;
ft_shared->start_time_real.sec = 0;
ft_shared->start_time_real.nsec = -1;
ft_shared->start_time_mon.sec = 0;
ft_shared->start_time_mon.nsec = -1;
ft_shared->start_time_mon_raw.sec = 0;
ft_shared->start_time_mon_raw.nsec = -1;
#ifdef CLOCK_BOOTTIME
ft_shared->start_time_boot.sec = 0;
ft_shared->start_time_boot.nsec = -1;
#endif
if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
{
@@ -319,16 +332,16 @@ int main (int argc, char **argv)
exit(EXIT_FAILURE);
}
if (sem_post(sem) == -1)
if (ft_sem_unlock(&wrapper_sem) == -1)
{
perror("faketime: semop");
perror("faketime: ft_sem_unlock");
cleanup_shobjs();
exit(EXIT_FAILURE);
}
snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
setenv("FAKETIME_SHARED", shared_objs, true);
sem_close(sem);
ft_sem_close(&wrapper_sem);
}
{

View File

@@ -38,6 +38,13 @@ struct system_time_s
#endif
};
/* Fixed-width time representation for shared memory (arch-independent) */
struct ft_shared_time_s
{
int64_t sec;
int64_t nsec;
};
/* Data shared among faketime-spawned processes */
struct ft_shared_s
{
@@ -47,8 +54,13 @@ struct ft_shared_s
uint64_t ticks;
/* Index of timestamp to be loaded from file */
uint64_t file_idx;
/* System time Faketime started at */
struct system_time_s start_time;
/* System time Faketime started at (fixed-width for cross-arch safety) */
struct ft_shared_time_s start_time_real;
struct ft_shared_time_s start_time_mon;
struct ft_shared_time_s start_time_mon_raw;
#ifdef CLOCK_BOOTTIME
struct ft_shared_time_s start_time_boot;
#endif
};
/* These are all needed in order to properly build on OSX */

227
src/ft_sem.c Normal file
View File

@@ -0,0 +1,227 @@
/*
* 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
* Free Software Foundation.
*
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License v2 along
* with the libfaketime; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "ft_sem.h"
#if FT_SEMAPHORE_BACKEND == FT_SYSV
#include <sys/sem.h>
#endif
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
#include <sys/file.h>
#endif
/*
* =======================================================================
* Semaphore related functions === SEM
* =======================================================================
*/
#if FT_SEMAPHORE_BACKEND == FT_SYSV
int ft_sem_name2key(char *name)
{
key_t key;
char fullname[256];
snprintf(fullname, sizeof(fullname), "/tmp%s", name);
fullname[sizeof(fullname) - 1] = '\0';
int fd = open(fullname, O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0)
{
perror("libfaketime: open");
return -1;
}
close(fd);
if (-1 == (key = ftok(fullname, 'F')))
{
perror("libfaketime: ftok");
return -1;
}
return key;
}
#endif
#if FT_SEMAPHORE_BACKEND == FT_FLOCK
static int ft_sem_name_to_path(const char *name, char *path, size_t pathlen)
{
const char *prefix = "/faketime_sem_";
const char *p = strstr(name, prefix);
if (p == NULL)
{
return -1;
}
const char *pid_str = p + strlen(prefix);
const char *tmpdir;
#ifdef __ANDROID__
tmpdir = getenv("TMPDIR");
if (tmpdir == NULL) tmpdir = "/data/local/tmp";
#else
tmpdir = "/dev/shm";
#endif
snprintf(path, pathlen, "%s/faketime_lock_%s", tmpdir, pid_str);
path[pathlen - 1] = '\0';
return 0;
}
#endif
int ft_sem_create(char *name, ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
if (SEM_FAILED == (ft_sem->sem = sem_open(name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
{
return -1;
}
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
key_t key = ft_sem_name2key(name);
int nsems = 1;
ft_sem->semid = semget(key, nsems, IPC_CREAT | IPC_EXCL | S_IWUSR | S_IRUSR);
if (ft_sem->semid >= 0) { /* we got here first */
struct sembuf sb = {
.sem_num = 0,
.sem_op = 1, /* number of resources the semaphore has (when decremented down to 0 the semaphore will block) */
.sem_flg = 0,
};
/* the number of semaphore operation structures (struct sembuf) passed to semop */
int nsops = 1;
/* do a semop() to "unlock" the semaphore */
if (-1 == semop(ft_sem->semid, &sb, nsops)) {
return -1;
}
} else if (errno == EEXIST) { /* someone else got here before us */
return -1;
} else {
return -1;
}
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
char path[256];
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
{
return -1;
}
ft_sem->fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IWUSR|S_IRUSR);
if (ft_sem->fd < 0)
{
return -1;
}
#endif
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
return 0;
}
int ft_sem_open(char *name, ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
if (SEM_FAILED == (ft_sem->sem = sem_open(name, 0)))
{
return -1;
}
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
key_t key = ft_sem_name2key(name);
ft_sem->semid = semget(key, 1, S_IWUSR | S_IRUSR);
if (ft_sem->semid < 0) {
return -1;
}
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
char path[256];
if (ft_sem_name_to_path(name, path, sizeof(path)) < 0)
{
return -1;
}
ft_sem->fd = open(path, O_RDWR);
if (ft_sem->fd < 0)
{
return -1;
}
#endif
strncpy(ft_sem->name, name, sizeof ft_sem->name - 1);
ft_sem->name[sizeof ft_sem->name - 1] = '\0';
return 0;
}
int ft_sem_lock(ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
return sem_wait(ft_sem->sem);
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
struct sembuf sb = {
.sem_num = 0,
.sem_op = -1, /* allocate resource (lock) */
.sem_flg = 0,
};
return semop(ft_sem->semid, &sb, 1);
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
return flock(ft_sem->fd, LOCK_EX);
#endif
}
int ft_sem_unlock(ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
return sem_post(ft_sem->sem);
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
struct sembuf sb = {
.sem_num = 0,
.sem_op = 1, /* free resource (unlock) */
.sem_flg = 0,
};
return semop(ft_sem->semid, &sb, 1);
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
return flock(ft_sem->fd, LOCK_UN);
#endif
}
int ft_sem_close(ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
return sem_close(ft_sem->sem);
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
/* NOP -- there's no "close" equivalent */
(void)ft_sem;
return 0;
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
int ret = close(ft_sem->fd);
ft_sem->fd = -1;
return ret;
#endif
}
int ft_sem_unlink(ft_sem_t *ft_sem)
{
#if FT_SEMAPHORE_BACKEND == FT_POSIX
return sem_unlink(ft_sem->name);
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
return semctl(ft_sem->semid, 0, IPC_RMID);
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
char path[256];
if (ft_sem_name_to_path(ft_sem->name, path, sizeof(path)) < 0)
{
return -1;
}
return unlink(path);
#endif
}

62
src/ft_sem.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* 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
* Free Software Foundation.
*
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License v2 along
* with the libfaketime; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef FT_SEM_H
#define FT_SEM_H
/* semaphore backend options */
#define FT_POSIX 1
#define FT_SYSV 2
#define FT_FLOCK 3
/* set default backend */
#ifndef FT_SEMAPHORE_BACKEND
#define FT_SEMAPHORE_BACKEND FT_FLOCK
#endif
/* validate selected backend */
#if FT_SEMAPHORE_BACKEND == FT_POSIX
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
#else
#error "Unknown FT_SEMAPHORE_BACKEND; select between FT_POSIX, FT_SYSV, and FT_FLOCK"
#endif
#if FT_SEMAPHORE_BACKEND == FT_POSIX
#include <semaphore.h>
#endif
typedef struct
{
char name[256];
#if FT_SEMAPHORE_BACKEND == FT_POSIX
sem_t *sem;
#elif FT_SEMAPHORE_BACKEND == FT_SYSV
int semid;
#elif FT_SEMAPHORE_BACKEND == FT_FLOCK
int fd;
#endif
} ft_sem_t;
int ft_sem_create(char *name, ft_sem_t *ft_sem);
int ft_sem_open(char *name, ft_sem_t *ft_sem);
int ft_sem_lock(ft_sem_t *ft_sem);
int ft_sem_unlock(ft_sem_t *ft_sem);
int ft_sem_close(ft_sem_t *ft_sem);
int ft_sem_unlink(ft_sem_t *ft_sem);
#endif /* FT_SEM_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
## Copyright (c) 2013, adrelanos at riseup dot net
## All rights reserved.

View File

@@ -1,18 +1,21 @@
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
OBJ = ${SRC:.c=.o}
TEST_SNIPPETS = $(notdir $(basename $(wildcard snippets/*.c)))
EXPECTATIONS= $(notdir $(basename $(wildcard snippets/*.variable)))
EXPECTATIONS = $(notdir $(basename $(wildcard snippets/*.variable)))
ALL_TESTS = timetest test
ALL_TESTS = timetest test shm_layout_test
ifneq ($(filter -DINTERCEPT_SYSCALL,${CFLAGS}),)
ALL_TESTS += confirm_variadic_promotion
else
TEST_SNIPPETS := $(filter-out syscall%,${TEST_SNIPPETS})
EXPECTATIONS := $(filter-out syscall%,${EXPECTATIONS})
endif
all: $(ALL_TESTS)
@@ -23,7 +26,10 @@ all: $(ALL_TESTS)
timetest: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS}
test: timetest functest
shm_layout_test: shm_layout_test.c ../src/faketime_common.h
${CC} -o $@ ${CFLAGS} $<
test: timetest functest libmallocintercept.so
@echo
@./test.sh
@@ -37,6 +43,9 @@ functest:
randomtest: repeat_random
./randomtest.sh
libmallocintercept.so: libmallocintercept.c
${CC} -shared -o $@ -fpic ${CFLAGS} $<
# ensure our variadic argument unpacking/repacking works as expected
confirm_variadic_promotion: variadic_promotion
./variadic_promotion
@@ -70,7 +79,7 @@ use_lib_%: _use_lib_test.c snippets/%.c lib%.so
## cleanup and metainformation
clean:
@rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o
@rm -f ${OBJ} timetest shm_layout_test getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o repeat_random libmallocintercept.so
distclean: clean
@echo

View File

@@ -66,7 +66,7 @@ get_monotonic_time()
clock_id=$1; shift;
FAKETIME_DONT_FAKE_MONOTONIC=${dont_fake_mono} \
fakecmd "2014-07-21 09:00:00" \
/bin/bash -c "for i in 1 2; do \
bash -c "for i in 1 2; do \
perl -w -MTime::HiRes=clock_gettime,${clock_id} -E \
'say clock_gettime(${clock_id})'; \
sleep 1; \

View File

@@ -0,0 +1,79 @@
# Tests for FAKETIME_FOLLOW_ABSOLUTE feature.
#
# When FAKETIME_FOLLOW_ABSOLUTE=1 is set alongside FAKETIME="%" and
# FAKETIME_FOLLOW_FILE, time freezes at the follow file's mtime
# and only advances when the file's mtime changes.
FOLLOW_FILE=".follow_absolute_test_file"
init()
{
typeset testsuite="$1"
PLATFORM=$(platform)
if [ -z "$PLATFORM" ]; then
echo "$testsuite: unknown platform! quitting"
return 1
fi
echo "# PLATFORM=$PLATFORM"
return 0
}
run()
{
init
run_testcase follow_absolute_basic
run_testcase follow_absolute_freeze
run_testcase follow_absolute_tracks_mtime
rm -f "$FOLLOW_FILE"
}
# Helper to run a command with follow-absolute configuration
follow_absolute_cmd()
{
FAKETIME_FOLLOW_FILE="$FOLLOW_FILE" \
FAKETIME_FOLLOW_ABSOLUTE=1 \
fakecmd "%" "$@"
}
# Test that time matches the follow file's mtime
follow_absolute_basic()
{
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
typeset actual
actual=$(follow_absolute_cmd date -u +"%Y-%m-%d %H:%M:%S")
asserteq "$actual" "2020-03-15 10:30:00" \
"time should match follow file mtime"
}
# Test that time stays frozen (does not advance with real time)
follow_absolute_freeze()
{
touch -d "2020-03-15 10:30:00 UTC" "$FOLLOW_FILE"
typeset timestamps
timestamps=$(follow_absolute_cmd \
perl -e 'print time(), "\n"; sleep(2); print time(), "\n"')
typeset first second
first=$(echo "$timestamps" | head -1)
second=$(echo "$timestamps" | tail -1)
asserteq "$first" "$second" \
"time should stay frozen within a single process"
}
# Test that time tracks file mtime changes at millisecond precision
follow_absolute_tracks_mtime()
{
touch -d "2020-03-15 10:30:00.000 UTC" "$FOLLOW_FILE"
typeset first
first=$(follow_absolute_cmd \
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
touch -d "2020-03-15 10:30:00.005 UTC" "$FOLLOW_FILE"
typeset second
second=$(follow_absolute_cmd \
perl -MTime::HiRes=time -e 'printf "%.3f\n", time()')
assertneq "$first" "$second" \
"time should advance with file mtime (ms precision)"
}

View File

@@ -0,0 +1,31 @@
# Verify that shared memory works end-to-end across processes.
# The faketime wrapper creates SHM with a known FAKETIME value,
# and the child process (via LD_PRELOAD) reads it and reports
# the faked time.
init()
{
typeset testsuite="$1"
PLATFORM=$(platform)
if [ -z "$PLATFORM" ]; then
echo "$testsuite: unknown platform! quitting"
return 1
fi
echo "# PLATFORM=$PLATFORM"
return 0
}
run()
{
init
run_testcase shm_year_check
}
shm_year_check()
{
typeset expected="2020"
typeset actual
actual=$(fakecmd "2020-06-15 12:00:00" date -u +%Y)
asserteq "$actual" "$expected" "child process should see faked year via SHM"
}

85
test/libmallocintercept.c Normal file
View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2022 be.storaged GmbH
*
* This file is part of libfaketime
*
* 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
* Free Software Foundation.
*
* libfaketime is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License v2 along
* with the libfaketime; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static void print_msg(const char *msg) {
size_t out;
out = write(0, msg, strlen(msg));
(void) out; /* unused */
}
static void* actual_malloc(size_t size) {
/* We would like to use "the real malloc", but cannot. Thus, this
* implements a trivial, allocate-only bump allocator to make things
* work.
*/
static char memory_arena[16 << 20];
static size_t allocated_index = 0;
void *result = &memory_arena[allocated_index];
allocated_index += size;
/* align to a multiple of 8 bytes */
allocated_index = (allocated_index + 7) / 8 * 8;
if (allocated_index >= sizeof(memory_arena)) {
print_msg("libmallocintercept is out of memory!");
abort();
}
return result;
}
static void poke_faketime(void) {
#ifdef FAIL_PRE_INIT_CALLS
/* To complicate things for libfaketime, this calls clock_gettime()
* while holding a lock. This should simulate problems that occurred
* with address sanitizer.
*/
static pthread_mutex_t time_mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec timespec;
pthread_mutex_lock(&time_mutex);
clock_gettime(CLOCK_REALTIME, &timespec);
pthread_mutex_unlock(&time_mutex);
#else
print_msg("FAIL_PRE_INIT_CALLS not defined, skipping poke_faketime() ");
#endif
}
void *malloc(size_t size) {
print_msg("Called malloc() from libmallocintercept...");
poke_faketime();
print_msg("successfully\n");
return actual_malloc(size);
}
void free(void *ptr) {
(void) ptr; /* unused */
print_msg("Called free() on from libmallocintercept...");
poke_faketime();
print_msg("successfully\n");
/* We cannot actually free memory */
}

65
test/shm_layout_test.c Normal file
View File

@@ -0,0 +1,65 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include "../src/faketime_common.h"
static int failures = 0;
static void check_offset(const char *field, size_t actual, size_t expected)
{
if (actual != expected)
{
fprintf(stderr, "FAIL: offsetof(ft_shared_s, %s) = %zu, expected %zu\n",
field, actual, expected);
failures++;
}
else
{
printf("OK: offsetof(ft_shared_s, %s) = %zu\n", field, actual);
}
}
static void check_size(const char *name, size_t actual, size_t expected)
{
if (actual != expected)
{
fprintf(stderr, "FAIL: sizeof(%s) = %zu, expected %zu\n",
name, actual, expected);
failures++;
}
else
{
printf("OK: sizeof(%s) = %zu\n", name, actual);
}
}
int main()
{
/* ft_shared_time_s must be exactly 16 bytes: two int64_t */
check_size("ft_shared_time_s", sizeof(struct ft_shared_time_s), 16);
/* Field offsets in ft_shared_s */
check_offset("ticks", offsetof(struct ft_shared_s, ticks), 0);
check_offset("file_idx", offsetof(struct ft_shared_s, file_idx), 8);
check_offset("start_time_real", offsetof(struct ft_shared_s, start_time_real), 16);
check_offset("start_time_mon", offsetof(struct ft_shared_s, start_time_mon), 32);
check_offset("start_time_mon_raw", offsetof(struct ft_shared_s, start_time_mon_raw), 48);
#ifdef CLOCK_BOOTTIME
check_offset("start_time_boot", offsetof(struct ft_shared_s, start_time_boot), 64);
check_size("ft_shared_s", sizeof(struct ft_shared_s), 80);
#else
check_size("ft_shared_s", sizeof(struct ft_shared_s), 64);
#endif
if (failures > 0)
{
fprintf(stderr, "%d layout check(s) failed\n", failures);
return EXIT_FAILURE;
}
printf("All layout checks passed\n");
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,32 @@
/* Test raw syscall(__NR_clock_nanosleep): relative and TIMER_ABSTIME */
#ifdef __NR_clock_nanosleep
struct timespec start_fake, end_fake_rel, end_fake_abs;
struct timespec req_rel = {0, 200 * 1000 * 1000}; /* 200ms fake sleep */
struct timespec req_abs;
long ret;
/* Capture starting time (fake view) */
clock_gettime(CLOCK_REALTIME, &start_fake);
/* Relative sleep via syscall */
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, 0, &req_rel, NULL);
clock_gettime(CLOCK_REALTIME, &end_fake_rel);
/* Absolute sleep target: 300ms after start_fake *./test_variable_data.sh */
req_abs.tv_sec = start_fake.tv_sec;
req_abs.tv_nsec = start_fake.tv_nsec + 300 * 1000 * 1000;
if (req_abs.tv_nsec >= 1000000000) { req_abs.tv_sec += 1; req_abs.tv_nsec -= 1000000000; }
/* Absolute sleep via syscall */
ret = syscall(__NR_clock_nanosleep, CLOCK_REALTIME, TIMER_ABSTIME, &req_abs, NULL);
clock_gettime(CLOCK_REALTIME, &end_fake_abs);
/* Report durations (fake view). */
long rel_ns = (end_fake_rel.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_rel.tv_nsec - start_fake.tv_nsec);
long abs_ns = (end_fake_abs.tv_sec - start_fake.tv_sec) * 1000000000L + (end_fake_abs.tv_nsec - start_fake.tv_nsec);
printf("[%s] syscall(clock_nanosleep) relative 200ms -> ~%ld ns, absolute +300ms -> ~%ld ns\n", where, rel_ns, abs_ns);
return ret;
#else
printf("[%s] __NR_clock_nanosleep not defined on this platform\n", where);
#endif

View File

@@ -0,0 +1 @@
FAKETIME 2020-01-01 00:00:00

View File

@@ -1,2 +1,2 @@
time_t t = time(NULL);
printf("[%s] time() yielded %zd\n", where, t);
printf("[%s] time() yielded %lu\n", where, (unsigned long)t);

View File

@@ -1,6 +1,7 @@
#!/bin/sh
FTPL="${FAKETIME_TESTLIB:-../src/libfaketime.so.1}"
MALLOC_INTERCEPT=./libmallocintercept.so
if [ -f /etc/faketimerc ] ; then
echo "Running the test program with your system-wide default in /etc/faketimerc"
@@ -62,6 +63,24 @@ echo "\$ LD_PRELOAD=$FTPL FAKETIME=\"-15d\" date"
LD_PRELOAD="$FTPL" FAKETIME="-15d" date
echo
echo "============================================================================="
echo
echo "Running the test program with malloc interception"
echo "\$ LD_PRELOAD=./libmallocintercept.so:$FTPL ./timetest"
LD_PRELOAD="./libmallocintercept.so:$FTPL" ./timetest
echo
echo "============================================================================="
echo
echo "@2005-03-29 14:14:14" > .faketimerc-for-test
echo "Running the test program with malloc interception and file faketimerc"
echo "\$ FAKETIME_NO_CACHE=1 FAKETIME_TIMESTAMP_FILE=.faketimerc-for-test LD_PRELOAD=./libmallocintercept.so:$FTPL ./timetest"
FAKETIME_NO_CACHE=1 FAKETIME_TIMESTAMP_FILE=.faketimerc-for-test LD_PRELOAD="./libmallocintercept.so:$FTPL" ./timetest
rm .faketimerc-for-test
echo
echo "============================================================================="
echo "Testing finished."

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e

View File

@@ -1,4 +1,4 @@
#! /bin/bash
#!/usr/bin/env bash
# testframe.sh DIR
# bare-bones testing framework.
# run the test suites in the given DIR;

View File

@@ -19,6 +19,7 @@
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -39,11 +40,131 @@
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <utime.h>
#define MONO_FIX_TIMEOUT_SECONDS 1
#define MONO_FIX_TOLERANCE_SECONDS 0.25 // Increased tolerance slightly for CI environments
#define MONO_FIX_LOWER_BOUND (MONO_FIX_TIMEOUT_SECONDS - MONO_FIX_TOLERANCE_SECONDS)
#define MONO_FIX_UPPER_BOUND (MONO_FIX_TIMEOUT_SECONDS + MONO_FIX_TOLERANCE_SECONDS)
#define VERBOSE 0
#define SIG SIGUSR1
#ifdef __ARM_ARCH
static int fake_monotonic_clock = 0;
#else
static int fake_monotonic_clock = 1;
#endif
static void test_utime_now(void)
{
char path[] = "/tmp/libfaketime-utime-XXXXXX";
int fd;
fd = mkstemp(path);
if (fd == -1)
{
perror("mkstemp");
exit(EXIT_FAILURE);
}
if (utime(path, NULL) == -1)
{
perror("utime(NULL)");
close(fd);
unlink(path);
exit(EXIT_FAILURE);
}
if (utimes(path, NULL) == -1)
{
perror("utimes(NULL)");
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("utime()/utimes(): NOW handling passed\n");
}
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)
{
@@ -57,36 +178,53 @@ handler(int sig, siginfo_t *si, void *uc)
}
}
static void get_fake_monotonic_setting(int* current_value)
{
char *tmp_env;
if ((tmp_env = getenv("FAKETIME_DONT_FAKE_MONOTONIC")) != NULL
|| (tmp_env = getenv("DONT_FAKE_MONOTONIC")) != NULL)
{
if (0 == strcmp(tmp_env, "1"))
{
(*current_value) = 0;
}
else
{
(*current_value) = 1;
}
}
}
void* pthread_test(void* args)
{
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t fake_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fake_cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t monotonic_cond;
pthread_condattr_t attr;
struct timespec timeToWait, now;
struct timespec time_to_wait, now;
int rt;
args = args; // silence compiler warning about unused argument
clock_gettime(CLOCK_REALTIME, &now);
timeToWait.tv_sec = now.tv_sec+1;
timeToWait.tv_nsec = now.tv_nsec;
time_to_wait.tv_sec = now.tv_sec+1;
time_to_wait.tv_nsec = now.tv_nsec;
printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
printf("(Intentionally sleeping 1 second...)\n");
fflush(stdout);
pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
pthread_mutex_lock(&fake_mtx);
rt = pthread_cond_timedwait(&fake_cond, &fake_mtx, &time_to_wait);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
pthread_mutex_unlock(&fakeMutex);
pthread_mutex_unlock(&fake_mtx);
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fakeMutex);
pthread_mutex_unlock(&fake_mtx);
pthread_condattr_init(&attr);
@@ -94,24 +232,63 @@ void* pthread_test(void* args)
pthread_cond_init(&monotonic_cond, &attr);
clock_gettime(CLOCK_MONOTONIC, &now);
timeToWait.tv_sec = now.tv_sec+1;
timeToWait.tv_nsec = now.tv_nsec;
time_to_wait.tv_sec = now.tv_sec+1;
time_to_wait.tv_nsec = now.tv_nsec;
printf("pthread_cond_timedwait: CLOCK_MONOTONIC test\n");
printf("(Intentionally sleeping 1 second...)\n");
printf("(If this test hangs for more than a few seconds, please report\n your glibc version and system details as FORCE_MONOTONIC_FIX\n issue at https://github.com/wolfcw/libfaketime)\n");
fflush(stdout);
pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
pthread_mutex_lock(&fake_mtx);
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
pthread_mutex_unlock(&fakeMutex);
pthread_mutex_unlock(&fake_mtx);
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fakeMutex);
pthread_mutex_unlock(&fake_mtx);
get_fake_monotonic_setting(&fake_monotonic_clock);
if (!fake_monotonic_clock)
{
printf("pthread_cond_timedwait: using real CLOCK_MONOTONIC test\n");
struct timespec mono_after_wait;
clock_gettime(CLOCK_MONOTONIC, &now);
time_to_wait.tv_sec = now.tv_sec + MONO_FIX_TIMEOUT_SECONDS;
time_to_wait.tv_nsec = now.tv_nsec;
struct timespec current_real_time;
clock_gettime(CLOCK_REALTIME, &current_real_time);
struct timespec new_real_time = current_real_time;
new_real_time.tv_sec += 3600; // Advance by one hour
clock_settime(CLOCK_REALTIME, &new_real_time);
pthread_mutex_lock(&fake_mtx);
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
clock_gettime(CLOCK_MONOTONIC, &mono_after_wait);
pthread_mutex_unlock(&fake_mtx);
double elapsed_wall_time = (mono_after_wait.tv_sec - now.tv_sec) +
(mono_after_wait.tv_nsec - now.tv_nsec) / 1000000000.0;
if (rt == ETIMEDOUT && (elapsed_wall_time >= MONO_FIX_LOWER_BOUND && elapsed_wall_time <= MONO_FIX_UPPER_BOUND))
{
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC passed: elapsed time = %.2f seconds\n", elapsed_wall_time);
}
else
{
printf("pthread_cond_timedwait with real CLOCK_MONOTONIC FAILED: elapsed time = %.2f seconds, return code = %d\n", elapsed_wall_time, rt);
exit(EXIT_FAILURE);
}
}
pthread_cond_destroy(&monotonic_cond);
return NULL;
@@ -237,6 +414,8 @@ printf("%s", 0 == 1 ? argv[0] : "");
printf("gettimeofday() : Current date and time: %s", ctime(&tv.tv_sec));
#ifndef __APPLE__
test_utime_now();
test_utimens_now();
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
{
perror("sigprocmask");