1 Commits

Author SHA1 Message Date
Wolfgang Hommel
46a0f84c1e Intecept pthread_cond_clockwait (addresses #353) 2022-02-20 21:51:28 +01:00
27 changed files with 444 additions and 2314 deletions

View File

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

19
NEWS
View File

@@ -1,23 +1,4 @@
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)

44
README
View File

@@ -1,5 +1,5 @@
libfaketime, version 0.9.12 (June 2025)
=======================================
libfaketime, version 0.9.10 (February 2022)
===========================================
Content of this file:
@@ -103,28 +103,6 @@ documentation whether it can be achieved by using libfaketime directly.
FORCE_MONOTONIC_FIX alone does not solve the hang on the MONOTONIC_CLOCK
test.
If FORCE_MONOTONIC_FIX was not set as a compile-time flag, you can also
set an environment variable FAKETIME_FORCE_MONOTONIC_FIX=1 if you want
to enable the fix at run-time, or to 0 if you explicitly want to disable
it. The fix is automatically enabled if libfaketime was compiled on a
system with glibc as the underlying libc implementation, and a glibc
version is detected at run-time that is assumed to need this workaround.
Please use Github issues at https://github.com/wolfcw/libfaketime/issues
to report any observed hangs during CLOCK_MONOTONIC tests and report
your CPU architecture, libc implementation (e.g., glibc 2.30) and any
other details that might help (e.g., Linux distribution, use within, e.g.,
Docker containers etc.).
Please try to avoid compiling with FORCE_MONOTONIC_FIX on platforms that
do not need it. While it won't make a difference in most cases, depending
on the specific FAKETIME settings in use, it would cause certain
intercepted functions such as pthread_cond_timedwait() return with a
time-out too early or too late, which could break some applications.
Try compiling without FORCE_MONOTONIC_FIX first and check whether the
tests appear to hang. If they do, you can either set the
FAKETIME_FORCE_MONOTONIC_FIX environment variable to 1, or re-compile
with FORCE_MONOTONIC_FIX set.
3. Installation
---------------
@@ -256,10 +234,10 @@ the difference:
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
FAKETIME_DONT_RESET=1 \
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
/bin/bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
LD_PRELOAD=src/libfaketime.so.1 FAKETIME="@2000-01-01 11:12:13" \
bash -c 'while [ $SECONDS -lt 5 ]; do date; sleep 1; done'
/bin/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 +332,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" \
bash -c 'while true; do echo $SECONDS ; sleep 1 ; done'
/bin/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,15 +398,6 @@ 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
-----------------------------------------
@@ -495,9 +464,6 @@ 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,50 +166,3 @@ 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" "June 2025" "faketime 0.9.12" wolfcw
.TH FAKETIME "1" "February 2022" "faketime 0.9.10" wolfcw
.SH NAME
faketime \- manipulate the system time for a given command
.SH SYNOPSIS
@@ -32,19 +32,16 @@ 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' 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'
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'
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,24 +49,13 @@
#
# INTERCEPT_SYSCALL
# - (On GNU/Linux only) intercept glibc's syscall() for known relevant syscalls.
# If enabled, this currently only works a few types of syscalls,
# including FUTEX (see below), clock_gettime, etc.
# If enabled, this currently only works to divert the getrandom syscall.
#
# - 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
@@ -95,11 +84,6 @@
# - 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
#
@@ -126,55 +110,20 @@ PREFIX ?= /usr/local
LIBDIRNAME ?= /lib/faketime
PLATFORM ?=$(shell uname)
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
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)
ifeq ($(PLATFORM),SunOS)
CFLAGS += -D__EXTENSIONS__ -D_XOPEN_SOURCE=600
endif
ifdef IS_ANDROID
CFLAGS += -D__ANDROID__
endif
LIB_LDFLAGS += -shared
LDFLAGS += $(FAKETIME_LINK_FLAGS)
LDFLAGS += $(FAKETIME_LINK_FLAGS) -lpthread
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
LDADD += -ldl -lm -lrt
BIN_LDFLAGS += -lrt
SRC = libfaketime.c
LIBS_OBJ = libfaketime.o libfaketimeMT.o
@@ -187,20 +136,17 @@ all: ${LIBS} ${BINS}
libfaketimeMT.o: EXTRA_FLAGS := -DPTHREAD_SINGLETHREADED_TIME
ft_sem.o: ft_sem.c ft_sem.h
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} $<
${LIBS_OBJ}: libfaketime.c ft_sem.h
${LIBS_OBJ}: libfaketime.c
${CC} -o $@ -c ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $<
%.so.${SONAME}: %.o ft_sem.o libfaketime.map
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ft_sem.o ${LDADD}
%.so.${SONAME}: %.o libfaketime.map
${CC} -o $@ -Wl,-soname,$@ ${LDFLAGS} ${LIB_LDFLAGS} $< ${LDADD}
${BINS}: faketime.c ft_sem.o ft_sem.h
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ft_sem.o ${LDFLAGS} ${BIN_LDFLAGS}
${BINS}: faketime.c
${CC} -o $@ ${CFLAGS} ${CPPFLAGS} ${EXTRA_FLAGS} $< ${LDFLAGS} ${BIN_LDFLAGS}
clean:
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS} ft_sem.o
@rm -f ${LIBS_OBJ} ${LIBS} ${BINS}
distclean: clean
@echo

View File

@@ -56,20 +56,7 @@ 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.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)
LIB_LDFLAGS += -dynamiclib -current_version 0.9.10 -compatibility_version 0.7
SONAME = 1
LIBS = libfaketime.${SONAME}.dylib
@@ -77,26 +64,14 @@ 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} libfaketime.arm64e.dylib libfaketime.arm64.dylib
@rm -f ${OBJ} ${LIBS} ${BINS}
distclean: clean
@echo

View File

@@ -1,51 +0,0 @@
#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.12
* This file is part of libfaketime, version 0.9.10
*
* 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,12 +44,11 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include "ft_sem.h"
#include <semaphore.h>
#include "android_compat.h"
#include "faketime_common.h"
const char version[] = "0.9.12";
const char version[] = "0.9.10";
#if (defined __APPLE__) || (defined __sun)
static const char *date_cmd = "gdate";
@@ -61,7 +60,6 @@ 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)
{
@@ -79,18 +77,15 @@ 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' 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"
"%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"
"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"
@@ -100,9 +95,9 @@ void usage(const char *name)
/** Clean up shared objects */
static void cleanup_shobjs()
{
if (-1 == ft_sem_unlink(&wrapper_sem))
if (-1 == sem_unlink(sem_name))
{
perror("faketime: ft_sem_unlink");
perror("faketime: sem_unlink");
}
if (-1 == shm_unlink(shm_name))
{
@@ -155,14 +150,6 @@ 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++;
@@ -257,8 +244,9 @@ int main (int argc, char **argv)
curr_opt++;
{
/* create lock and shared memory */
/* create semaphores and shared memory */
int shm_fd;
sem_t *sem;
struct ft_shared_s *ft_shared;
char shared_objs[PATH_BUFSIZE * 2 + 1];
@@ -272,10 +260,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 (-1 == ft_sem_create(sem_name, &wrapper_sem))
if (SEM_FAILED == (sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IWUSR|S_IRUSR, 1)))
{
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");
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");
exit(EXIT_FAILURE);
}
@@ -283,12 +271,15 @@ 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");
ft_sem_unlink(&wrapper_sem);
if (-1 == sem_unlink(argv[2]))
{
perror("faketime: sem_unlink");
}
exit(EXIT_FAILURE);
}
/* set shm size */
if (-1 == ftruncate(shm_fd, sizeof(struct ft_shared_s)))
if (-1 == ftruncate(shm_fd, sizeof(uint64_t)))
{
perror("faketime: ftruncate");
cleanup_shobjs();
@@ -304,9 +295,9 @@ int main (int argc, char **argv)
exit(EXIT_FAILURE);
}
if (ft_sem_lock(&wrapper_sem) == -1)
if (sem_wait(sem) == -1)
{
perror("faketime: ft_sem_lock");
perror("faketime: sem_wait");
cleanup_shobjs();
exit(EXIT_FAILURE);
}
@@ -314,16 +305,12 @@ 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.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
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;
if (-1 == munmap(ft_shared, (sizeof(struct ft_shared_s))))
{
@@ -332,16 +319,16 @@ int main (int argc, char **argv)
exit(EXIT_FAILURE);
}
if (ft_sem_unlock(&wrapper_sem) == -1)
if (sem_post(sem) == -1)
{
perror("faketime: ft_sem_unlock");
perror("faketime: semop");
cleanup_shobjs();
exit(EXIT_FAILURE);
}
snprintf(shared_objs, sizeof(shared_objs), "%s %s", sem_name, shm_name);
setenv("FAKETIME_SHARED", shared_objs, true);
ft_sem_close(&wrapper_sem);
sem_close(sem);
}
{

View File

@@ -38,13 +38,6 @@ 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
{
@@ -54,13 +47,8 @@ struct ft_shared_s
uint64_t ticks;
/* Index of timestamp to be loaded from file */
uint64_t file_idx;
/* 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
/* System time Faketime started at */
struct system_time_s start_time;
};
/* These are all needed in order to properly build on OSX */

View File

@@ -1,227 +0,0 @@
/*
* 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
}

View File

@@ -1,62 +0,0 @@
/*
* 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 @@
#!/usr/bin/env bash
#!/bin/bash
## Copyright (c) 2013, adrelanos at riseup dot net
## All rights reserved.

View File

@@ -1,21 +1,18 @@
CC = gcc
CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS) -U_FILE_OFFSET_BITS -U_TIME_BITS
CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra $(FAKETIME_COMPILE_CFLAGS)
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 shm_layout_test
ALL_TESTS = timetest 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)
@@ -26,10 +23,7 @@ all: $(ALL_TESTS)
timetest: ${OBJ}
${CC} -o $@ ${OBJ} ${LDFLAGS}
shm_layout_test: shm_layout_test.c ../src/faketime_common.h
${CC} -o $@ ${CFLAGS} $<
test: timetest functest libmallocintercept.so
test: timetest functest
@echo
@./test.sh
@@ -43,9 +37,6 @@ 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
@@ -79,7 +70,7 @@ use_lib_%: _use_lib_test.c snippets/%.c lib%.so
## cleanup and metainformation
clean:
@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
@rm -f ${OBJ} timetest getrandom_test syscall_test $(foreach f,${TEST_SNIPPETS},use_lib_${f} lib${f}.so run_${f}) variadic_promotion variadic/*.o
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" \
bash -c "for i in 1 2; do \
/bin/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

@@ -1,79 +0,0 @@
# 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

@@ -1,31 +0,0 @@
# 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"
}

View File

@@ -1,85 +0,0 @@
/*
* 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 */
}

View File

@@ -1,65 +0,0 @@
#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

@@ -1,32 +0,0 @@
/* 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

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

View File

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

View File

@@ -1,7 +1,6 @@
#!/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"
@@ -63,24 +62,6 @@ 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 @@
#!/usr/bin/env bash
#!/bin/bash
set -e

View File

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

View File

@@ -19,7 +19,6 @@
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
@@ -40,131 +39,11 @@
#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)
{
@@ -178,53 +57,36 @@ 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 fake_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fake_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t fakeMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t fakeCond = PTHREAD_COND_INITIALIZER;
pthread_cond_t monotonic_cond;
pthread_condattr_t attr;
struct timespec time_to_wait, now;
struct timespec timeToWait, now;
int rt;
args = args; // silence compiler warning about unused argument
clock_gettime(CLOCK_REALTIME, &now);
time_to_wait.tv_sec = now.tv_sec+1;
time_to_wait.tv_nsec = now.tv_nsec;
timeToWait.tv_sec = now.tv_sec+1;
timeToWait.tv_nsec = now.tv_nsec;
printf("pthread_cond_timedwait: CLOCK_REALTIME test\n");
printf("(Intentionally sleeping 1 second...)\n");
fflush(stdout);
pthread_mutex_lock(&fake_mtx);
rt = pthread_cond_timedwait(&fake_cond, &fake_mtx, &time_to_wait);
pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&fakeCond, &fakeMutex, &timeToWait);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
pthread_mutex_unlock(&fake_mtx);
pthread_mutex_unlock(&fakeMutex);
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fake_mtx);
pthread_mutex_unlock(&fakeMutex);
pthread_condattr_init(&attr);
@@ -232,63 +94,23 @@ void* pthread_test(void* args)
pthread_cond_init(&monotonic_cond, &attr);
clock_gettime(CLOCK_MONOTONIC, &now);
time_to_wait.tv_sec = now.tv_sec+1;
time_to_wait.tv_nsec = now.tv_nsec;
timeToWait.tv_sec = now.tv_sec+1;
timeToWait.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");
printf("(Intentionally sleeping 1 second..., see docs about CLOCK_MONOTONIC test)\n");
fflush(stdout);
pthread_mutex_lock(&fake_mtx);
rt = pthread_cond_timedwait(&monotonic_cond, &fake_mtx, &time_to_wait);
pthread_mutex_lock(&fakeMutex);
rt = pthread_cond_timedwait(&monotonic_cond, &fakeMutex, &timeToWait);
if (rt != ETIMEDOUT)
{
printf("pthread_cond_timedwait failed\n");
pthread_mutex_unlock(&fake_mtx);
pthread_mutex_unlock(&fakeMutex);
exit(EXIT_FAILURE);
}
pthread_mutex_unlock(&fake_mtx);
pthread_mutex_unlock(&fakeMutex);
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;
@@ -414,8 +236,6 @@ 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");