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

Reply via email to