Skip to content

Intercept lstat64, fstat64, fstatat and fstatat64 (glibc >= 2.33) - #565

Merged
wolfcw merged 1 commit into
wolfcw:masterfrom
charys117:fix-glibc-2.33-stat-entry-points
Oct 27, 2026
Merged

wolfcw merged 1 commit into
wolfcw:masterfrom
charys117:fix-glibc-2.33-stat-entry-points

Conversation

@charys117

Copy link
Copy Markdown
Contributor

Fixes #477.

Since glibc 2.33, fstatat, fstatat64, fstat64 and lstat64 are exported from libc as regular functions (@@GLIBC_2.33), and programs built against a newer glibc call them directly rather than going through __fxstatat, __fxstatat64, __fxstat64 and __lxstat64. libfaketime only intercepts the __*xstat* wrappers for these, so with FAKE_STAT they return real file times while stat() returns faked ones. Python is one common case: os.scandir() / DirEntry.stat() use fstatat, and so does os.stat(..., dir_fd=...).

This PR adds wrappers for the four functions next to the existing stat64() wrapper. They use the same STAT_HANDLER / STAT64_HANDLER macros and the same __GLIBC__ / _TIME_BITS != 64 guards, with the fstatat* pair also under NO_ATFILE. The real functions are looked up with dlsym(RTLD_NEXT, ...) like the others. On glibc < 2.33 these symbols are not exported by libc (programs get them from libc_nonshared.a, which calls the __*xstat* functions), so behaviour there does not change.

It also adds test/stat_entry_points_test.c and test/functests/test_stat_entry_points.sh. The test sets a file's mtime and checks that stat, lstat, fstat and, on glibc, fstatat, stat64, lstat64, fstat64 and fstatat64 all report the faked mtime under FAKETIME=+1d.

Reproducer

The reproducer from #477, extended to the other three functions:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>

int main() {
	struct stat st;
	struct stat64 st64;
	int fd = open("1", O_RDONLY);

	stat("1", &st);
	printf("stat      %ld\n", (long)st.st_mtime);
	fstatat(AT_FDCWD, "1", &st, 0);
	printf("fstatat   %ld\n", (long)st.st_mtime);
	fstatat64(AT_FDCWD, "1", &st64, 0);
	printf("fstatat64 %ld\n", (long)st64.st_mtime);
	fstat64(fd, &st64);
	printf("fstat64   %ld\n", (long)st64.st_mtime);
	lstat64("1", &st64);
	printf("lstat64   %ld\n", (long)st64.st_mtime);
	return 0;
}

With touch -d '2020-01-01 00:00:00 UTC' 1 and FAKETIME=-1d LD_PRELOAD=src/libfaketime.so.1:

              master        this PR
stat      1577750400     1577750400
fstatat   1577836800     1577750400
fstatat64 1577836800     1577750400
fstat64   1577836800     1577750400
lstat64   1577836800     1577750400

Python:

$ FAKETIME=-1d LD_PRELOAD=src/libfaketime.so.1 python3 -c '...'
master   stat 1577750400  scandir 1577836800  dir_fd 1577836800
this PR  stat 1577750400  scandir 1577750400  dir_fd 1577750400

Testing

  • make test passes on Ubuntu 24.04 (glibc 2.39) and Debian 13 (glibc 2.41), on both x86_64 and aarch64. The new test fails against master's library on all four, for fstatat, lstat64, fstat64 and fstatat64.
  • test/docker_baseline.sh alpine:3.20 (musl) passes. The glibc-only checks are skipped there.
  • glibc 2.31 (Ubuntu 20.04, aarch64): the library and the new test built by hand, since master's Makefile needs a newer make there. master and this PR give the same, already faked, results.
  • test/release_check.sh and test/compile_abi_variants.sh (gcc and clang) pass, a clang build passes the new test, and the macOS build (Makefile.OSX) still compiles.

Since glibc 2.33, the stat family is exported from libc as regular
functions, and programs built against it call fstatat(), fstatat64(),
fstat64() and lstat64() directly instead of going through the
__fxstatat*() / __fxstat64() / __lxstat64() wrappers. libfaketime only
hooked the old wrappers for these, so with FAKE_STAT the times they
return were not faked. Python's os.scandir() / DirEntry.stat() and
os.stat(dir_fd=...) are common examples.

Add wrappers for the four functions next to stat64(), using the existing
STAT_HANDLER / STAT64_HANDLER, and a functional test that checks every
stat entry point reports the faked mtime.

Fixes wolfcw#477
@wolfcw
wolfcw merged commit fb4e8e5 into wolfcw:master Oct 27, 2026
20 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fstatat() not intercepted (with glibc >= 2.33?)

2 participants