Open-source project
wolfcw/libfaketime avatar
wolfcw/libfaketime

libfaketime: shifting one process's clock with the linker, and where that trick stops working

libfaketime modifies the system time for a single application

3,125 stars380 forksCGPL-2.0

At a glance

What is it?
A preload library that intercepts the time syscalls a program uses, which handles the ordinary cases cleanly and then tells you exactly which runtimes bypass it and why.
Who is it for?
libfaketime is the right tool for a C or Python program whose expiry logic, certificate validation or scheduled job you need to exercise at another date, and its documentation is unusually honest about the boundaries, which is the main reason to read it before anything else. It is the wrong tool when the target is a language runtime that caches its clock, and the README names Go, Python, .NET, Boost.Asio, jemalloc, Proton and the JVM as cases where that can happen.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 11 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One process sees a different clock, and nothing else does

libfaketime intercepts the system calls that programs use to retrieve the current date and time, and reports modified dates and times instead, as specified by you. That framing is the whole design. You do not change the system clock, you do not need root, and no other process on the machine is affected. If your application needs to see that a licence expired last year or that a certificate is valid for another six months, you run it under this library and nothing else in your system notices.

The specification is flexible in two directions. You can give an absolute date such as 01/01/2004, or a relative one such as 10 days ago. The README lists three intended uses: deterministic build processes, debugging time related issues such as expired SSL certificates, and testing software for year 2038 compliance. That last one is the least common and the most interesting, because it is about a future hardware limitation rather than a present bug.

The mechanism is the library preload facility of the platform's linker, which is involved in starting programs. That single sentence in the README determines the whole compatibility surface, and the documentation returns to it repeatedly. A wrapper script called `faketime` is included for convenience, with the README stating plainly that it does not expose all of the library's functionality and that you should check whether your case can be achieved with libfaketime directly if the wrapper does not cover it.

Version 0.9.13 dated August 2026 is the current one, matching the latest release tag, and the repository is GPL-2.0 licensed with the `COPYING` file at the root.

The compatibility section is the documentation worth reading first

Most projects list what they work with. This one lists what they do not, at length, and the list is more useful than a feature list.

Statically linked binaries and setuid binaries are out, because the preload mechanism cannot reach them. The README names `ping` and `passwd` as examples of the kind of program this affects, and defers to the system linker's manual page for the details.

Threaded programs get a separate library. `libfaketimeMT.so.1` is built with the pthread synchronization calls and single threads calls through the `time()` intercept, because several variables are statically cached and could cause problems when accessed without synchronization. The README is candid that the performance penalty might be an issue for some applications, and gives you an out: remove `-DPTHREAD_SINGLETHREADED_TIME` from the Makefile and rebuild `libfaketimeMT.so.1` to get an unsynchronized intercept.

Java and JVM applications work but need `FAKETIME_DONT_FAKE_MONOTONIC` passed as an extra argument, because without it the java command usually hangs.

The most fundamental limitation is architectural. Applications that dynamically load system libraries such as librt themselves, instead of relying on the linker at startup, bypass the interception. The README says this happens a lot in complex runtimes, names programs written in Go and some JVM implementations, and states that since libfaketime is effectively bypassed in such situations there is nothing the library can do about it. It asks instead that runtime vendors implement their environments so selected system calls can be intercepted through LD_PRELOAD.

Applications can also be built to actively prevent libfaketime from working, by checking whether certain environment variables are set or whether libfaketime specific files are present. The README names that pattern rather than treating it as a bug.

The expanded runtime compatibility paragraph goes further, naming Go, Python, .NET, Boost.Asio, jemalloc users, Proton with Wine, and JVMs as cases that may cache clocks, use vDSO or direct system calls, load libraries after startup, or combine real and faked clock domains. It asks for a minimal C reproducer using the same clock API before treating an application specific report as a libfaketime bug, and it states that such limitations are compatibility constraints rather than a security boundary.

CI coverage as a proxy for how widely it is actually used

The README's opening section describes continuous integration coverage, which is unusual detail for a project of this size and tells you what the maintainer considers a supported platform.

The required CI gates cover macOS on arm64 and arm64e, and Linux across glibc and musl, 32-bit with time64, ARM64, and the distributions Fedora, Debian, Rocky Linux, CentOS Stream, openSUSE and Arch Linux. The documented C snippets are compiled and run in CI, including the optional deterministic random interfaces, and sanitizer and container jobs are time-bounded and upload complete logs when a run fails.

That last detail is worth noticing. Time-bounded jobs with complete log upload on failure is a project treating its own test suite as something that can hang, which is consistent with the rest of the documentation.

For a bounded local run the README points at `test/docker_baseline.sh` with a compatible image, giving `gcc:13-bookworm` as an example. Two environment variables control it: `DOCKER_PLATFORM` when the image architecture has to be selected explicitly, and `LIBFAKETIME_RUN_OPTIONAL_RUNTIME_TESTS=1` when the optional Rust runtime probe and its toolchain are available.

The runtime checks are described as deliberately optional, and the reasoning given is the right one: some language runtimes use vDSO or direct system calls and can bypass LD_PRELOAD or dyld interposition, and a bypass is reported as a compatibility limitation rather than treated as proof of a core libfaketime failure.

The repository tree is small and conventional: `src/` for the library, `test/` for the suite, `man/` for manual pages, and `README.OSX`, `README.developers` and `README.packagers` as separate documents for the three audiences that build this thing. The `NEWS` file and a `TODO` complete the root. The last push was on 2026-09-17, after the 0.9.13 release on 2026-08-31, and the repository is not archived.

Diagnosing a hang rather than a wrong time

The documentation treats hangs as the primary symptom and gives you a decision procedure, which is more useful than it first appears.

Running `make test` performs a series of tests after the library compiles. On some platforms the CLOCK_MONOTONIC test appears to hang forever. If and only if that happens on your platform, the documented fix is to add the CFLAG `-DFORCE_MONOTONIC_FIX` to `src/Makefile` and recompile. The instruction is emphatic about the condition: do not set FORCE_MONOTONIC_FIX on platforms where the test does not hang, since the fix changes behaviour precisely to paper over a platform quirk.

If you see hangs on the CLOCK_REALTIME test instead, the fix is `-DFORCE_PTHREAD_NONVER`, and the documentation says to also set FORCE_PTHREAD_NONVER when FORCE_MONOTONIC_FIX alone does not solve a MONOTONIC_CLOCK test hang.

The compile-time flag has a runtime counterpart. `FAKETIME_FORCE_MONOTONIC_FIX=1` enables the fix at run time, or `0` disables it explicitly, for cases where you did not set the compile-time flag. The documentation also says the fix is enabled automatically when libfaketime was compiled against glibc and a glibc version is detected at run time that is assumed to need the workaround.

That automatic detection is the kind of detail that saves an afternoon, and it is also the kind of detail that explains a confusing result: a build on one libc behaving differently from a build on another, with the difference not visible in any flag you passed.

The Makefile at the root is a conventional GNU one with a platform selector that picks `Makefile.OSX` when `uname` reports Darwin, targets for all, test, check, install, uninstall, clean and distclean, and an install step that also installs the manual pages and copies the README and NEWS into a documentation directory under the prefix.

Against time namespaces, libfaketime's neighbours and system clocks

The alternative approach on modern Linux is time namespaces, `unshare --time`, which gives a process and its children a virtualized monotonic and boottime clock without touching wall time at all. The key difference is that namespaces change monotonic and boottime time, not the wall clock that programs read from `time()` and `gettimeofday()`, which is the thing most expiry logic depends on. libfaketime targets the wall clock through interception, which is why it reaches programs namespaces leave alone and why it also has the hang and bypass problems namespaces do not.

There is also the original `faketime` command by David Maltese, which wraps a command and shifts the system clock for the duration, requiring privileges and affecting everything on the machine. libfaketime's README and the repository's own topic tags refer to it, and the practical distinction is the one above: per process and unprivileged here, system wide and privileged there.

The third neighbour is faking the clock in the application, through a library passed to the test harness or a dependency injected time source. That is the cleanest approach when you control the code, because it avoids the whole interception layer, and it is what you should reach for in a codebase you maintain. libfaketime earns its place when you cannot change the program: a closed binary, a third party service, or an interpreter you would rather not instrument.

For deterministic builds specifically, `SOURCE_DATE_EPOCH` is the right tool, and libfaketime lists deterministic builds as a use case without claiming to be the answer for them. Knowing which of these three problems you actually have is the first decision, and it decides the tool.

Editorial conclusion

libfaketime is the right tool for a C or Python program whose expiry logic, certificate validation or scheduled job you need to exercise at another date, and its documentation is unusually honest about the boundaries, which is the main reason to read it before anything else. It is the wrong tool when the target is a language runtime that caches its clock, and the README names Go, Python, .NET, Boost.Asio, jemalloc, Proton and the JVM as cases where that can happen. Build it with make, check your target against the runtime compatibility list in the README, and if a hang appears during the CLOCK_MONOTONIC test, apply the FORCE_MONOTONIC_FIX the documentation describes rather than assuming the library is broken.

Frequently asked questions

What is libfaketime and how does it work?

libfaketime intercepts the system calls that programs use to retrieve the current date and time and reports modified dates and times instead, as you specify. It uses the library preload mechanism of the platform's linker, so only the process you launch under it sees the shifted clock.

Why does libfaketime not work with my program?

The most common cause is that the runtime bypasses interception, which the README says happens with programs that load system libraries themselves or that use vDSO or direct system calls, naming Go, some JVM implementations, Python, .NET and Boost.Asio among others. Static and setuid binaries also cannot be intercepted at all.

What is FAKETIME_DONT_FAKE_MONOTONIC for?

Java and JVM based applications need it passed as an extra argument, because the README states that without it the java command usually hangs. It tells the library to leave the monotonic clock alone while still shifting the wall clock.

How do I fix a libfaketime CLOCK_MONOTONIC test that hangs?

If and only if the test hangs on your platform, add the CFLAG -DFORCE_MONOTONIC_FIX to src/Makefile and recompile libfaketime. A runtime equivalent exists as FAKETIME_FORCE_MONOTONIC_FIX=1, and glibc builds enable the fix automatically when a glibc version needing the workaround is detected.

How does libfaketime compare to unshare --time?

They change different clocks. Time namespaces virtualize the monotonic and boottime clocks without touching wall time, while libfaketime shifts the wall clock that programs read through time and gettimeofday, which is what most expiry logic uses. libfaketime reaches more programs and has the corresponding bypass and hang caveats.

Official sources

  1. License: GPL-2.0
  2. Project website
  3. README
  4. Releases
  5. wolfcw/libfaketime on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wolfcw-libfaketime.svg)](https://hysenlabs.com/projects/wolfcw-libfaketime)