Package: faketime
X-Debbugs-Cc: [email protected]
Version: 0.9.12-0.1
Severity: normal
When using start-at faketime mode (`FAKETIME='@...'`), libfaketime may
pass invalid explicit file timestamps to file timestamp update calls,
causing them to fail with EINVAL.
This showed up while running autopkgtest with libfaketime preloaded,
where dpkg-source -x fails during source unpacking with:
dpkg-source: error: cannot change timestamp for
manual-repro/.pc/applied-patches: Invalid argument
A reliable reproducer is:
NO_FAKE_STAT=1 \
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 \
FAKETIME='@2037-01-01 00:00:00' \
dpkg-source -x ./<artifact>.dsc manual-repro
This fails with exit status 22.
Tracing the failure shows the failing syscall is:
utimensat(AT_FDCWD, "manual-repro/.pc/applied-patches",
[{tv_sec=339616344, tv_nsec=94346438164267000},
{tv_sec=339616344, tv_nsec=94346438164267000}], 0) = -1 EINVAL
So the kernel is receiving an already-invalid explicit timespec array,
with tv_nsec far outside the valid range.
I am sending this notice and proceeding with the upload without delay.
This NMU is being done under the maintainer's LowThresholdNmu preference.
Please find the debdiff for the proposed NMU attached.
diff -Nru faketime-0.9.12/debian/changelog faketime-0.9.12/debian/changelog
--- faketime-0.9.12/debian/changelog 2026-03-28 14:53:05.000000000 -0300
+++ faketime-0.9.12/debian/changelog 2026-03-30 15:13:11.000000000 -0300
@@ -1,3 +1,12 @@
+faketime (0.9.12-0.2) UNRELEASED; urgency=medium
+
+ * Non-maintainer upload.
+ * d/patches:
+ - Add a patch series with merged utime/utimes fixes
+ - Add fix-blhc-test-build-flags.patch
+
+ -- Aquila Macedo Costa <[email protected]> Mon, 30 Mar 2026 15:13:11
-0300
+
faketime (0.9.12-0.1) unstable; urgency=medium
* Non-maintainer upload.
diff -Nru
faketime-0.9.12/debian/patches/add-regression-test-utime-utimes-now.patch
faketime-0.9.12/debian/patches/add-regression-test-utime-utimes-now.patch
--- faketime-0.9.12/debian/patches/add-regression-test-utime-utimes-now.patch
1969-12-31 21:00:00.000000000 -0300
+++ faketime-0.9.12/debian/patches/add-regression-test-utime-utimes-now.patch
2026-03-30 15:13:11.000000000 -0300
@@ -0,0 +1,83 @@
+From: Aquila Macedo <[email protected]>
+Date: Mon, 30 Mar 2026 09:56:22 -0300
+Subject: Add regression coverage for utime and utimes "now" handling
+Origin: upstream, 4aa0077bfc2503dc42c34ee69e95b7c5a6948930
+
+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.
+
+Backported to Debian by: Aquila Macedo Costa <[email protected]>.
+---
+ test/timetest.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+
+diff --git a/test/timetest.c b/test/timetest.c
+index cd52cdd..9266820 100644
+--- a/test/timetest.c
++++ b/test/timetest.c
+@@ -40,11 +40,56 @@
+ #include <pthread.h>
+ #include <errno.h>
+ #include <signal.h>
++#include <utime.h>
+
+ #define VERBOSE 0
+
+ #define SIG SIGUSR1
+
++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";
+@@ -301,6 +346,7 @@ 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)
+ {
diff -Nru faketime-0.9.12/debian/patches/fix-blhc-test-build-flags.patch
faketime-0.9.12/debian/patches/fix-blhc-test-build-flags.patch
--- faketime-0.9.12/debian/patches/fix-blhc-test-build-flags.patch
1969-12-31 21:00:00.000000000 -0300
+++ faketime-0.9.12/debian/patches/fix-blhc-test-build-flags.patch
2026-03-30 15:13:11.000000000 -0300
@@ -0,0 +1,80 @@
+From: Aquila Macedo <[email protected]>
+Date: Mon, 30 Mar 2026 12:42:45 -0300
+Subject: Honor standard build flags in test Makefile
+
+Pass CPPFLAGS and LDFLAGS through the test build rules so externally
+provided compiler and linker flags are applied consistently.
+
+This fixes the Debian blhc warnings, but the change is not specific to
+Debian and improves portability of the test build in general.
+---
+ test/Makefile | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/test/Makefile b/test/Makefile
+index 01cbd9f..07f0ed3 100644
+--- a/test/Makefile
++++ b/test/Makefile
+@@ -1,4 +1,4 @@
+-CC = gcc
++CC ?= gcc
+
+ CFLAGS += -std=gnu99 -Wall -DFAKE_STAT -Werror -Wextra
$(FAKETIME_COMPILE_CFLAGS) -U_FILE_OFFSET_BITS -U_TIME_BITS
+ LDFLAGS += -lrt -lpthread
+@@ -21,7 +21,7 @@ endif
+ all: $(ALL_TESTS)
+
+ .c.o:
+- ${CC} -c ${CFLAGS} $<
++ ${CC} -c ${CPPFLAGS} ${CFLAGS} $<
+
+ timetest: ${OBJ}
+ ${CC} -o $@ ${OBJ} ${LDFLAGS}
+@@ -35,21 +35,21 @@ functest:
+ ./testframe.sh functests
+
+ %_test: %_test.c
+- ${CC} -o $@ ${CFLAGS} $<
++ ${CC} -o $@ ${CPPFLAGS} ${CFLAGS} $< ${LDFLAGS}
+
+ randomtest: repeat_random
+ ./randomtest.sh
+
+ libmallocintercept.so: libmallocintercept.c
+- ${CC} -shared -o $@ -fpic ${CFLAGS} $<
++ ${CC} -shared -o $@ -fpic ${CPPFLAGS} ${CFLAGS} $< ${LDFLAGS}
+
+ # ensure our variadic argument unpacking/repacking works as expected
+ confirm_variadic_promotion: variadic_promotion
+ ./variadic_promotion
+ variadic_promotion: variadic/main.o variadic/outer.o variadic/inner.o
+- ${CC} -o $@ ${CFLAGS} $^
++ ${CC} -o $@ ${CFLAGS} $^ ${LDFLAGS}
+ variadic/%.o: variadic/%.c
+- ${CC} -c -o $@ ${CFLAGS} $<
++ ${CC} -c -o $@ ${CPPFLAGS} ${CFLAGS} $<
+
+ # run snippet tests
+ snippets: test_variable_data test_library_constructors
+@@ -59,7 +59,7 @@ test_variable_data: test_variable_data.sh $(foreach
f,${EXPECTATIONS},run_${f})
+ ./test_variable_data.sh ${EXPECTATIONS}
+
+ run_%: _run_test.c snippets/%.c
+- sed s/SNIPPET_NAME/$*/g < _run_test.c | ${CC} -o $@ ${CFLAGS} -x c -
++ sed s/SNIPPET_NAME/$*/g < _run_test.c | ${CC} -o $@ ${CPPFLAGS}
${CFLAGS} -x c - ${LDFLAGS}
+
+ ## test snippets in other library constructors:
+ test_library_constructors: $(foreach f,${TEST_SNIPPETS},test_lib_${f})
+@@ -67,10 +67,10 @@ test_lib_%: test_constructors.sh use_lib_% lib%.so
+ ./test_constructors.sh $*
+
+ lib%.so: _libtest.c snippets/%.c
+- sed s/SNIPPET_NAME/$*/g < _libtest.c | ${CC} -shared -o $@ -fpic
${CFLAGS} -x c -
++ sed s/SNIPPET_NAME/$*/g < _libtest.c | ${CC} -shared -o $@ -fpic
${CPPFLAGS} ${CFLAGS} -x c - ${LDFLAGS}
+
+ use_lib_%: _use_lib_test.c snippets/%.c lib%.so
+- sed s/SNIPPET_NAME/$*/g < _use_lib_test.c | ${CC} -L. -o $@ ${CFLAGS}
-x c - -l$*
++ sed s/SNIPPET_NAME/$*/g < _use_lib_test.c | ${CC} -L. -o $@ ${CPPFLAGS}
${CFLAGS} -x c - -l$* ${LDFLAGS}
+
+
+ ## cleanup and metainformation
diff -Nru faketime-0.9.12/debian/patches/fix-utime-utimes-fake-now.patch
faketime-0.9.12/debian/patches/fix-utime-utimes-fake-now.patch
--- faketime-0.9.12/debian/patches/fix-utime-utimes-fake-now.patch
1969-12-31 21:00:00.000000000 -0300
+++ faketime-0.9.12/debian/patches/fix-utime-utimes-fake-now.patch
2026-03-30 15:13:11.000000000 -0300
@@ -0,0 +1,80 @@
+From: Aquila Macedo <[email protected]>
+Date: Mon, 30 Mar 2026 09:53:41 -0300
+Subject: Fix fake "now" handling for utime and utimes
+Origin: upstream, 097ce79771fe94f7e887984c154eecb3d362c585
+
+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
+---
+ src/libfaketime.c | 30 ++++++++++++++++++++++++++++--
+ 1 file changed, 28 insertions(+), 2 deletions(-)
+
+diff --git a/src/libfaketime.c b/src/libfaketime.c
+index 0e3d1ce..9cd7aa1 100644
+--- a/src/libfaketime.c
++++ b/src/libfaketime.c
+@@ -330,6 +330,8 @@ static pthread_once_t initialized_once_control =
PTHREAD_ONCE_INIT;
+ /* prototypes */
+ static int fake_gettimeofday(struct timeval *tv);
+ static int fake_clock_gettime(clockid_t clk_id, struct timespec *tp);
++static int fake_current_realtime(struct timespec *tp);
++static int fake_current_timeval(struct timeval *tv);
+ int read_config_file();
+ bool str_array_contains(const char *haystack, const char *needle);
+ void *ft_dlvsym(void *handle, const char *symbol, const char *version, const
char *full_name, char *ignore_list, bool should_debug_dlsym);
+@@ -1188,7 +1190,12 @@ int utime(const char *filename, const struct utimbuf
*times)
+ {
+ if (times == NULL)
+ { /* The user wants their given fake times left alone but they requested
NOW, so turn it into fake NOW */
+- ntbuf.actime = ntbuf.modtime = time(NULL);
++ struct timespec now;
++ if (fake_current_realtime(&now) == -1)
++ {
++ return -1;
++ }
++ ntbuf.actime = ntbuf.modtime = now.tv_sec;
+ times = &ntbuf;
+ }
+ }
+@@ -1221,7 +1228,10 @@ int utimes(const char *filename, const struct timeval
times[2])
+ {
+ if (times == NULL)
+ { /* The user wants their given fake times left alone but they requested
NOW, so turn it into fake NOW */
+- fake_gettimeofday(&tn[0]);
++ if (fake_current_timeval(&tn[0]) == -1)
++ {
++ return -1;
++ }
+ tn[1] = tn[0];
+ times = tn;
+ }
+@@ -1256,6 +1266,22 @@ static int fake_current_realtime(struct timespec *tp)
+ return fake_clock_gettime(CLOCK_REALTIME, tp);
+ }
+
++static int fake_current_timeval(struct timeval *tv)
++{
++ struct timespec ts;
++ int result;
++
++ result = fake_current_realtime(&ts);
++ if (result == -1)
++ {
++ return -1;
++ }
++
++ tv->tv_sec = ts.tv_sec;
++ tv->tv_usec = ts.tv_nsec / 1000;
++ return 0;
++}
++
+ /* This conditionally offsets 2 timespec values. The caller's out_times array
+ * always contains valid translated values, even if in_times was NULL. */
+ static int fake_two_timespec(const struct timespec in_times[2], struct
timespec out_times[2])
diff -Nru faketime-0.9.12/debian/patches/series
faketime-0.9.12/debian/patches/series
--- faketime-0.9.12/debian/patches/series 2026-03-28 14:53:05.000000000
-0300
+++ faketime-0.9.12/debian/patches/series 2026-03-30 15:13:11.000000000
-0300
@@ -1,2 +1,5 @@
fix-utimensat-futimens-fake-now-handling.patch
add-regression-test-for-utimens-now.patch
+fix-utime-utimes-fake-now.patch
+add-regression-test-utime-utimes-now.patch
+fix-blhc-test-build-flags.patch