wolfcw / libfaketime

libfaketime modifies the system time for a single application
https://github.com/wolfcw/libfaketime
GNU General Public License v2.0
2.62k stars 319 forks source link
datetime hacktoberfest intercept-calls ld-preload reproducible-builds wrapper

libfaketime, version 0.9.10 (March 2022)

Content of this file:

  1. Introduction

  2. Compatibility issues

  3. Installation

  4. Usage a) Basics b) Using absolute dates c) Using 'start at' dates d) Using offsets for relative dates e) Advanced features and caveats f) Faking the date and time system-wide g) Using the "faketime" wrapper script h) "Limiting" libfaketime based on elapsed time or number of calls i) "Limiting" libfaketime per process j) Spawning an external process k) Saving timestamps to file, loading them from file l) Replacing random numbers with deterministic number sequences

  5. License

  6. Contact

  7. Introduction

libfaketime intercepts various system calls that programs use to retrieve the current date and time. It then reports modified (faked) dates and times (as specified by you, the user) to these programs. This means you can modify the system time a program sees without having to change the time system-wide.

libfaketime allows you to specify both absolute dates (e.g., 01/01/2004) and relative dates (e.g., 10 days ago).

libfaketime might be used for various purposes, for example

libfaketime ships with a command line wrapper called "faketime" that makes it easier to use, but does not expose all of libfaketime's functionality. If your use case is not covered by the faketime command, make sure to look in this documentation whether it can be achieved by using libfaketime directly.

  1. Compatibility issues

  1. Installation

Running "make" compiles both library versions and a test program, which it then also executes.

If the test works fine, you should copy the libfaketime libraries (libfaketime.so.1, and libfaketimeMT.so.1) to the place you want them in. Running "make install" will attempt to place them in /usr/local/lib/faketime and will install the wrapper shell script "faketime" in /usr/local/bin, both of which most likely will require root privileges. However, from a technical point of view, there is no necessity for a system-wide installation, so you can use libfaketime also on machines where you do not have root privileges. You may want to adjust the PREFIX variable in the Makefiles accordingly.

By default, the Makefile compiles/links libfaketime for your default system architecture. If you need to build, e.g., 32-bit files on a 64-bit platform, please see the notes about CFLAGS and LDFLAGS in src/Makefile.

Since version 0.6, system calls to file timestamps are also intercepted, thanks to a contribution by Philipp Hachtmann. This is especially useful in combination with relative time offsets as explained in section 4d) below, if a program writes and reads files whose timestamps also shall be faked. If you do not need this feature or if it confuses the application you want to use FTPL with, define the environment variable NO_FAKE_STAT, and the intercepted stat calls will be passed through unaltered.

On macOS, it is necessary to compile differently, due to the different behavior dyld has. Use the Makefile.OSX file provided to compile libfaketime.1.dylib. Additionally, instead of using LD_PRELOAD, the variable DYLD_INSERT_LIBRARIES should be set to the path to libfaketime.1.dylib, and the variable DYLD_FORCE_FLAT_NAMESPACE should be set (to anything). macOS users should read README.OSX for additional details.

  1. Usage

4a) Usage basics

Using libfaketime on a program of your choice consists of two steps:

  1. Making sure libfaketime gets loaded by the system's linker.
  2. Specify the faked time.

As an example, we want the "date" command to report our faked time. To do so, we could use the following command line on Linux:

user@host> date Tue Nov 23 12:01:05 CEST 2016

user@host> LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d" date Mon Nov 8 12:01:12 CEST 2016

user@host> LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d" FAKETIME_DONT_FAKE_MONOTONIC=1 java -version java version "1.8.0_111" Java(TM) SE Runtime Environment (build 1.8.0_111-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

The basic way of running any command/program with libfaketime enabled is to make sure the environment variable LD_PRELOAD contains the path and filename of the libfaketime library. This can either be done by setting it once beforehand:

export LD_PRELOAD=/path/to/libfaketime.so.1 (now run any command you want)

Or it can be done by specifying it on the command line itself:

LD_PRELOAD=/path/to/libfaketime.so.1 your_command_here

(These examples are for the bash shell; how environment variables are set may vary on your system.)

On Linux, library search paths can be set as part of the linker configuration. LD_PRELOAD then also works with relative paths. For example, when libfaketime.so.1 is installed as /path/to/libfaketime.so.1, you can add /path/to to an appropriate linker configuration file, e.g., /etc/ld.so.conf.d/local.conf, and then run the "ldconfig" command. Afterwards, using LD_PRELOAD=libfaketime.so.1 suffices.

However, also the faked time should be specified; otherwise, libfaketime will be loaded, but just report the real system time. There are multiple ways to specify the faked time:

a) By setting the environment variable FAKETIME. b) By using the file given in the environment variable FAKETIME_TIMESTAMP_FILE c) By using the file .faketimerc in your home directory. d) By using the file /etc/faketimerc for a system-wide default. e) By using FAKETIME_UPDATE_TIMESTAMP_FILE and date -s "

If you want to use b) c) or d), $HOME/.faketimerc or /etc/faketimerc consist of only one line of text with exactly the same content as the FAKETIME environment variable, which is described below. Note that /etc/faketimerc will only be used if there is no $HOME/.faketimerc and no FAKETIME_TIMESTAMP_FILE file exists. Also, the FAKETIME environment variable always has priority over the files. For FAKETIME_UPDATE_TIMESTAMP_FILE please see below.

4b) Using absolute dates

The format that must be used for absolute dates is "YYYY-MM-DD hh:mm:ss". For example, the 24th of December, 2020, 8:30 PM would have to be specified as FAKETIME="2020-12-24 20:30:00".

4c) Using 'start at' dates

(Thanks to a major contribution by David North, TDI in version 0.7)

The format that must be used for _startat dates is "@YYYY-MM-DD hh:mm:ss". For example, the 24th of December, 2020, 8:30 PM would have to be specified as FAKETIME="@2020-12-24 20:30:00".

The absolute dates described in 4b) simulate a STOPPED system clock at the specified absolute time. The 'start at' format allows a 'relative' clock operation as described below in section 4d), but using a 'start at' time instead of an offset time.

If the started process itself starts other (child) processes, they by default will start with the specified start-at-date again. If this is not what you need, set the environment variable FAKETIME_DONT_RESET=1. Try these examples to see 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'

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'

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 even though all the "date" commands are new processes.

4d) Using offsets for relative dates

Relative date offsets can be positive or negative, thus what you put into FAKETIME must either start with a + or a -, followed by a number, and optionally followed by a multiplier:

You now should understand the complete example we've used before:

LD_PRELOAD=/usr/local/lib/libfaketime.so.1 FAKETIME="-15d" date

This command line makes sure libfaketime gets loaded and sets the faked time to 15 days in the past.

Moreno Baricevic has contributed support for the FAKETIME_FMT environment variable, which allows you to optionally set the strptime() format:

Some simple examples: LD_PRELOAD=./libfaketime.so.1 FAKETIME_FMT=%s FAKETIME="date +%s -d'1 year ago'" date LD_PRELOAD=./libfaketime.so.1 FAKETIME_FMT=%s FAKETIME="stat -c %Y somefile" date LD_PRELOAD=./libfaketime.so.1 FAKETIME_FMT=%c FAKETIME="date" date

4e) Advanced features and caveats

Advanced time specification options:

Since version 0.8, thanks to a contribution by Karl Chen, fractions can be used in the specification of time offsets. For example,

FAKETIME="+1,5h"

is equivalent to FAKETIME="+90m". Please be aware that the fraction delimiter depends on your locale settings, so actually you might need to use

FAKETIME="+1.5h"

You should figure out the proper delimiter, e.g., by using libfaketime on a command like /bin/date where you immediately can verify whether it worked as expected.

Also contributed by Karl Chen in v0.8 is the option to speed up or slow down the wall clock time for the program which is executed using libfaketime. For example,

FAKETIME="+1y x2"

will set the faked time one year into the future and will make the clock run twice as fast. Similarly,

FAKETIME="+1y x0,5"

will make the clock run only half as fast. As stated above, the fraction delimiter depends on your locale. Furthermore,

FAKETIME="+1y i2,0"

will make the clock step two seconds per each time(), etc. call, being completely independently of the system clock. It helps running programs with some determinism. 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 using "x" or "i" in FAKETIME still requires giving an offset (see section 4d). This means that "+1y x2" will work, but "x2" only will not. If you do not want to fake the time, but just modify clock speed, use something 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'

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 measuring the time since the shell was started).

(Please note that replacing "echo $SECONDS" e.g. with a call to "/bin/date" will not give the expected result, since /bin/date will always be started as a new process for which also libfaketime will be re-initialized. It will show the correct offset (1.5 years in the future), but no speed-ups or slow-downs.)

For applications that should use a different date & time each time they are run, consider using the included timeprivacy wrapper shellscript (contributed by adrelanos at riseup dot net).

Caveats:

Whenever possible, you should use relative offsets or 'start at' dates, and not use absolute dates.

Why? Because the absolute date/time you set is fixed, i.e., if a program retrieves the current time, and retrieves the current time again 5 minutes later, it will still get the same result twice. This is likely to break programs which measure the time passing by (e.g., a mail program which checks for new mail every X minutes).

Using relative offsets or 'start at' dates solves this problem. libfaketime then will always report the faked time based on the real current time and the offset you've specified.

Please also note that your default specification of the fake time is cached for 10 seconds in order to enhance the library's performance. Thus, if you change the content of $HOME/.faketimerc or /etc/faketimerc while a program is running, it may take up to 10 seconds before the new fake time is applied. If this is a problem in your scenario, you can change number of seconds before the file is read again with environment variable FAKETIME_CACHE_DURATION, or disable caching at all with FAKETIME_NO_CACHE=1. Remember that disabling the cache may negatively influence the performance (especially when not using FAKETIME environment but configuration files, such as FAKETIME_TIMESTAMP_FILE).

Setting FAKETIME by means of a file timestamp

Based on a proposal by Hitoshi Harada (umitanuki), the "start at" time can now be set through any file in the file system by setting the FAKETIME environment variable to "%" (a percent sign) and FAKETIME_FOLLOW_FILE to the name of the file whose modification timestamp shall be used as source for the "start at" time.

Usage example:

create any file with December 24th, 2009, 12:34:56 as timestamp

touch -t 0912241234.56 /tmp/my-demo-file.tmp

run a bash shell with an endless loop printing the current time

LD_PRELOAD=/path/to/libfaketime.so.1 \ FAKETIME='%' FAKETIME_FOLLOW_FILE=/tmp/my-demo-file.tmp \ FAKETIME_DONT_RESET=1 \ bash -c 'while true ; do date ; sleep 1 ; done'

now, while the above is running, change the file's timestamp

(in a different terminal window or whatever)

touch -t 2002290123.45 /tmp/my-demo-file.tmp

Changing the 'x' modifier during run-time

Using FAKETIME_TIMESTAMP_FILE allows for easily changing the FAKETIME setting while a program is running:

echo "+0 x1" > /tmp/my-faketime.rc LD_PRELOAD=libfaketime.so.1 FAKETIME_TIMESTAMP_FILE="/tmp/my-faketime.rc" \ FAKETIME_NO_CACHE=1 ./some-program & sleep 10 ; echo "+0 x10" > /tmp/my-faketime.rc

Changing the speed of the wall clock time, i.e., using a different 'x' modifier during run-time, by default can lead to greater jumps that may confuse the program. For example, if the program has been running for 10 seconds on 'x1', and then the setting is changed to 'x10', the faked time will look to the program as if it has been running for more than 100 instead of just more than 10 seconds.

By setting the environment variable FAKETIME_XRESET to any value, transitions between different 'x' modifier values will be significantly smoothed:

LD_PRELOAD=libfaketime.so.1 FAKETIME_TIMESTAMP_FILE="/tmp/my-faketime.rc" \ FAKETIME_NO_CACHE=1 FAKETIME_XRESET=1 ./some-program &

Setting FAKETIME_XRESET ensures that wall clock time begins to run faster only after the 'x' modifier has been changed (when increasing it) and also ensures that the reported faked time does not jump back to past values (when decreasing it).

Please note that FAKETIME_XRESET internally works by resetting libfaketime's internal time-keeping data structures, which may have side effects on reported file timestamps. Using FAKETIME_XRESET should be considered experimental at the moment.

Cleaning up shared memory

libfaketime uses semaphores and shared memory on platforms that support it in order to sync faketime settings across parent-child processes.

Please note that this does not share the time set by settimeofday (for that see FAKETIME_UPDATE_TIMESTAMP_FILE below).

libfaketime will clean up when it exits properly. However, when processes are terminated (e.g., by Ctrl-C on command line), shared memory cannot be cleaned up properly. In such cases, you should occasionally delete

/dev/shm/faketimeshm and /dev/shm/sem.faketimesem

manually (or properly automated). Leftover files there from processes that already have been terminated are not a problem in general, but result in a libfaketime error the next time a process is started with a process id for which such a stale semaphore/shared memory exists. Thus, if you run across the following error message

libfaketime: In ft_shm_create(), shm_open failed: File exists

please cleanup /dev/shm as described above. This is especially relevant 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

libfaketime can be compiled with the CFLAG "-DFAKE_SETTIME" in order to also intercept time-setting functions, i.e., clock_settime(), settimeofday(), and adjtime(). The FAKETIME environment variable will be adjusted on each call.

When the environment variable FAKETIME_TIMESTAMP_FILE is set, points to a writeable (creatable) custom config file and the environment variable FAKETIME_UPDATE_TIMESTAMP_FILE is "1", then the file also is updated on each call. By this, a common "virtual time" can be shared by several processes, where each can adjust the time for all.

Sharing "virtual settable time" between independent processes

When libfaketime was compiled with FAKETIME_COMPILE_CFLAGS="-DFAKE_SETTIME", it can be configured to support a common time offset for multiple processes. This for example allows to use "ntpdate" as normal user without affecting system clock, interactively testing software with different dates or testing complex software with multiple independent processes that themself use settime internally.

Examples:

$ export LD_PRELOAD=libfaketime.so.1 $ export FAKETIME_TIMESTAMP_FILE="/tmp/my-faketime.rc" $ export FAKETIME_UPDATE_TIMESTAMP_FILE=1 $ export FAKETIME_CACHE_DURATION=1 # in seconds

or: export FAKETIME_NO_CACHE=1

$ date -s "1999-12-24 16:00:00" Fri Dec 24 16:00:00 CET 1999 $ LD_PRELOAD="" date Thu Apr 9 15:19:38 CEST 2020 $ date Fri Dec 24 16:00:02 CET 1999 $ /usr/sbin/ntpdate -u clock.isc.org 9 Apr 15:18:37 ntpdate[718]: step time server xx offset 640390517.057257 sec $ date Thu Apr 9 15:18:40 CEST 2020

In another terminal, script or environmment the same variables could be set and the same time would be printed. This also avoid the need to directly update the rc config file to use different times, but of course only supports time offsets.

Please note that this feature is not compatible with several other features, such as FAKETIME_FOLLOW_FILE, FAKETIME_XRESET and maybe others. After first settime, offsets will be used in FAKETIME_TIMESTAMP_FILE, even if it initially used advanced time specification options.

4f) Faking the date and time system-wide

David Burley of SourceForge, Inc. reported an interesting use case of applying libfaketime system-wide: Currently, all virtual machines running inside an OpenVZ host have the same system date and time. In order to use multiple sandboxes with different system dates, the libfaketime library can be put into /etc/ld.so.preload; it will then be applied to all commands and programs automatically. This is of course best used with a system-wide /etc/faketimerc file. Kudos to SourceForge, Inc. for providing the patch!

Caveat: If you run a virtual machine, its real-time clock might be reset to the real world date & time when you reboot. Depending on your FAKETIME setting, this may lead to side effects, such as forced file system checks on each reboot. System-wide faked time may also lead to unexpected side effects with software auto-update tools, if the offset between real world time and faked system time is too large. If in doubt, set your system's date to the faked time and try out whether everything still works as expected before applying libfaketime system-wide.

4g) Using the "faketime" wrapper

As of version 0.8, libfaketime provides a command named "faketime", which is placed into /usr/bin by "make install". It spares the hassle of setting the LD_PRELOAD and FAKETIME environment variables manually, but only exposes a subset of libfaketime's functionality. On the other hand, it uses the date interpretation function by /bin/date in order to provide higher flexibility regarding the specification of the faked date and time. For example, you can use

faketime 'last Friday 5 pm' /your/command/here

Of course, also absolute dates can be used, such as in

faketime '2018-12-24 08:15:42' /bin/date

Thanks to Daniel Kahn Gillmor for providing these suggestions!

Balint Reczey has rewritten the wrapper in 0.9.5 from a simple shell script to an efficient wrapper application.

4h) "Limiting" libfaketime based on elapsed time or number of calls

Starting with version 0.9, libfaketime can be configured to not be continuously active, but only during a certain time interval.

For example, you might want to start a program with the real current time, but after 5 minutes of usage, you might want it to see a faked time, e.g., a year in the future.

Dynamic changes to the faked time are alternatively possible by

The feature described here works based on two pairs of environment variables,

FAKETIME_START_AFTER_SECONDS and FAKETIME_STOP_AFTER_SECONDS, and FAKETIME_START_AFTER_NUMCALLS and FAKETIME_STOP_AFTER_NUMCALLS

The default value for each of these environment variables is -1, which means "ignore this value".

If you want libfaketime to be only active during the run-time minutes 2 to 5 of your application, you would set

FAKETIME_START_AFTER_SECONDS=60 FAKETIME_STOP_AFTER_SECONDS=300

This means that your application will work with the real time from start (second 0) up to second 60. It will then see a faked time from run-time seconds 60 to 300 (minutes 2, 3, 4, and 5). After run-time second 600, it will again see the real (not-faked) time.

This approach is not as flexible as changing the FAKETIME environment variable during runtime, but may be easier to use, works on a per-program (and not a per-user or system-wide) scope, and has only a minor performance overhead.

Using the other pair of environment variables, you can limit the activity time of libfaketime not based on wall-clock seconds, but on the number of time-related function calls the started program performs. This alternative is probably only suitable for programmers who either know the code of the program in order to determine useful start/stop values or want to perform fuzzing tests.

Both pairs of environment variables can be combined to further restrict libfaketime activity, although this is only useful in very few scenarios.

Limiting libfaketime activity in this way is not recommended in general. Many programs will break when they are subject to sudden changes in time, especially if they are started using the current (real) time and are then sent back into the past after, e.g., 5 minutes. For example, they may appear to be frozen or stuck because they are waiting until a certain point in time that, however, is never reached due to the delayed libfaketime activity. Avoid using this functionality unless you are sure you really need it and know what you are doing.

4i) "Limiting" libfaketime per process

faketime can be instructed to fake time related calls only for selected commands or to fake time for each command except for a certain subset of commands.

The environment variables are FAKETIME_ONLY_CMDS and FAKETIME_SKIP_CMDS respectively.

Example: FAKETIME_ONLY_CMDS=javadoc faketime '2008-12-24 08:15:42' make will run the "make" command but the time faking will only be applied to javadoc processes.

Multiple commands are separated by commas.

Example: FAKETIME_SKIP_CMDS="javadoc,ctags" faketime '2008-12-24 08:15:42' make will run the "make" command and apply time faking for everything "make" does except for javadoc and ctags processes.

FAKETIME_ONLY_CMDS and FAKETIME_SKIP_CMDS are mutually exclusive, i.e., you cannot set them both at the same time. faketime will terminate with an error message if both environment variables are set.

4j) Spawning an external process

From version 0.9 on, libfaketime can execute a shell command once after a) an arbitrary number of seconds has passed or b) a number of time-related system calls has been made by the program since it started. This has two limitations one needs to be aware of:

Spawning the external process is controlled using three environment variables: FAKETIME_SPAWN_TARGET, FAKETIME_SPAWN_SECONDS, FAKETIME_SPAWN_NUMCALLS.

Example (using bash on Linux):

(... usual libfaketime setup here, setting LD_PRELOAD and FAKETIME ...) export FAKETIME_SPAWN_TARGET="/bin/echo 'Hello world'" export FAKETIME_SPAWN_SECONDS=5 /opt/local/bin/myprogram

This will run the "echo" command with the given parameter during the first time-related system function call that "myprogram" performs after running for 5 seconds.

4k) Saving timestamps to file, loading them from file

To store and load timestamp offsets using one and the same file allowing to share a common "virtual time" between independent processes, please see FAKETIME_UPDATE_TIMESTAMP_FILE above. The FAKETIME_SAVE_FILE feature is different.

faketime can save faked timestamps to a file specified by FAKETIME_SAVE_FILE environment variable. It can also use the file specified by FAKETIME_LOAD_FILE to replay timestamps from it. After consuming the whole file, libfaketime returns to using the rule set in FAKETIME variable, but the timestamp processes will start counting from will be the last timestamp in the file.

The file stores each timestamp in a stream of saved_timestamp structs without any metadata or padding:

/ Storage format for timestamps written to file. Big endian. / struct saved_timestamp { int64_t sec; uint64_t nsec; };

faketime needs to be run using the faketime wrapper to use these files. This functionality has been added by Balint Reczey in v0.9.5.

4l) Replacing random numbers with deterministic number sequences

libfaketime can be compiled with the CFLAG FAKE_RANDOM set (see src/Makefile).

When compiled this way, libfaketime additionally intercepts calls to the function getrandom(), which currently is Linux-specific.

This functionality is intended to feed a sequence of deterministic, repeatable numbers to applications, which use getrandom(), instead of the random numbers provided by /dev/[u]random.

For creating the deterministic number sequence, libfaketime internally uses Bernard Widynski's Middle Square Weyl Sequence Random Number Generator, see https://mswsrng.wixsite.com/rand.

It requires a 64-bit seed value, which has to be passed via the environment variable FAKERANDOM_SEED, as in, for example

LD_PRELOAD=src/libfaketime.so.1 \ FAKERANDOM_SEED="0x12345678DEADBEEF" \ test/getrandom_test

Whenever the same seed value is used, the same sequence of "random-looking" numbers is generated.

Please be aware that this definitely breaks any security properties that may be attributed to random numbers delivered by getrandom(), e.g., in the context of cryptographic operations. Use it for deterministic testing purposes only. Never use it in production.

For a discussion on why this apparently not date-/time-related function has been added to libfaketime and how it may evolve, see Github issue #275.

  1. License

libfaketime has been released under the GNU General Public License, GPL. Please see the included COPYING file.

  1. Contact

Bug reports, feature suggestions, success reports, and patches/pull requests are highly appreciated:

        https://github.com/wolfcw/libfaketime