fd_filestat_set_times writes corrupted timestamps to the host: nanoseconds as seconds
Describe the bug
Summary
fd_filestat_set_times (POSIX futimens) returns Errno::Success and writes an incorrect timestamp to the host file.
The host backend passes a WASI nanosecond timestamp into filetime::FileTime::from_unix_time, whose first argument is seconds. The nanosecond count is used verbatim as a second count. Requesting 1e9 ns (one second past the epoch) sets the host mtime to 1e9 seconds, i.e. 2001-09-09. Present-day timestamps overflow the representable range and saturate at the filesystem maximum: on ext4, 0x37FFFFFFF, the year 2446.
The guest-visible timestamp is correct: the in-memory inode stores the right nanosecond value. Only the value written through to the host is corrupted, so a guest that stats the file back will not notice.
Related: #6972 (path_filestat_set_times silently does nothing). Same code area.
Component: lib/virtual-fs (host_fs)
Affected versions: confirmed on 7.2.1 and 7.4.0
$ wasmer -vV
wasmer 7.4.0
binary: wasmer-cli
commit-hash: 32b50f8b600efa8e2d5f88593c453139bf1ca222
commit-date: 2026-08-31
host: x86_64-unknown-linux-gnu
CPU flags: sse2 sse3 ssse3 sse4.1 sse4.2 popcnt avx bmi bmi2 avx2 avx512dq avx512vl avx512f lzcnt fma
runtimes: Singlepass, Cranelift, LLVM, V8
features: wasix, napi_v10, napi_extension_wasmer_v0Steps to reproduce
Reproducer attached: wasmer-futimens-repro.zip. It needs only wasmer and python3.
unzip wasmer-futimens-repro.zip
cd wasmer-futimens-repro
./run.sh # or: ./run.sh /path/to/wasmerrun.sh creates a test file on a --volume-mounted host directory, opens it, calls fd_filestat_set_times with SET_ATIM|SET_MTIM, and prints the host-side mtime against the requested value. It repeats across several inputs to show the transformation.
Expected behavior
The host mtime matches the requested timestamp: a request for 1e18 ns yields a host mtime of 1000000000 s.
Actual behavior
requested (ns) = seconds host mtime (s) guest mtim (s)
1000000000 1 1000000000 1
2000000000 2 2000000000 2
1000000000000000000 1000000000 15032385535 1000000000
1788400000000000000 1788400000 15032385535 1788400000The first two rows show the mechanism plainly: the nanosecond count was used verbatim as a second count. The last two saturate at 15032385535 = 0x37FFFFFFF, the ext4 maximum timestamp.
Rendered as a date, a request for 2001-09-09 produces:
2446-05-10 22:38:55.000000000 +0000Note the last column: the guest-visible value is correct throughout. Only the host write is wrong.
Additional context
lib/virtual-fs/src/host_fs.rs:447:
fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
let atime = atime.map(|t| filetime::FileTime::from_unix_time(t as i64, 0));
let mtime = mtime.map(|t| filetime::FileTime::from_unix_time(t as i64, 0));
...
}atime and mtime arrive as nanoseconds — the VirtualFile trait documents its sibling accessors as "in nanoseconds as a UNIX timestamp", and mem_fs's set_times stores them as nanoseconds without conversion. FileTime::from_unix_time(secs, nanos) expects seconds.
Introduced in c47dd42f3 (2024-04-26).
Disclosure on AI Content
I used AI (Claude Opus) to assist with authoring this issue, and the reproducer code is entirely AI generated.
Source: wasmerio/wasmer