Repository navigation
fs: non-bigint stat can give 0.20566138856% bigger results #58726
Description
Activity
- addedfsIssues and PRs related to file-system APIs and the fs module.Issues and PRs related to file-system APIs and the fs module.
on Jun 16, 2025 After some investigation, it's not a Node.js/libuv/v8 bug but is still fixable on Node.js side.
The culprit waskMsPerSeccoefficient and its accomplice waskNsPerMs.The former had value of
10 ** 3 === 1002.05661388564544723(powf64(0x4024000000000000, 0x4008000000000000) == 0x408f5073f1fb1ac6), the latter had value of10 ** 6 === 998697.28497027803678066(powf64(0x4024000000000000, 0x4018000000000000) == 0x412e7a5291e79fd1).These results are not okay and normally nobody should get them, but we don't have to compute these values on every launch anyway, hence opened #58728 as fix.
The root cause was in glibc, somehow computing like this when using XMM registers and SSE instructions:
$ cat pew.c #include <stdio.h> #include <math.h> int main(void) { double a = 10.0; double b = 1.0; double c = pow(a, b); printf("pow(%.1f, %.1f) == %.16f\n", a, b, c); return 0; } $ gcc -lm -o pew pew.c -O0 && ./pew pow(10.0, 1.0) == 9.9887951003674988
...essentially corrupting any exponent that is not 2 and any base that is not power of 2.
Attempt to compile glibc with
-mfpmath=387failed:In file included from ../sysdeps/x86/fpu/fenv_private.h:495, from ../sysdeps/ieee754/dbl-64/e_remainder.c:37: ../sysdeps/ieee754/dbl-64/e_remainder.c: In function ‘__ieee754_remainder’: ../sysdeps/generic/fenv_private.h:291:10: error: cleanup argument not a function 291 | struct rm_ctx ctx __attribute__((cleanup (CLEANUPFUNC ## _ctx))); \ | ^~~~~~ ../sysdeps/generic/fenv_private.h:313:3: note: in expansion of macro ‘SET_RESTORE_ROUND_GENERIC’ 313 | SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noex, \ | ^~~~~~~~~~~~~~~~~~~~~~~~~ ../sysdeps/ieee754/dbl-64/e_remainder.c:58:7: note: in expansion of macro ‘SET_RESTORE_ROUND_NOEX’ 58 | SET_RESTORE_ROUND_NOEX (FE_TONEAREST); | ^~~~~~~~~~~~~~~~~~~~~~
So can't confirm yet, but I'm positive that normally
powand by extensionMath.powand**produce better results. 😅 In musl-based environments (SSE-capable Athlon XP and ARMv6l RPi) it works fine, with same versions of gcc andnode.- added a commit that references this issue
on Jun 18, 2025 - added a commit that references this issue
on Jun 23, 2025 - added 2 commits that reference this issue
on Jul 21, 2025
Version
v24.2.0, v25.0.0-pre
Platform
Subsystem
fs
What steps will reproduce the bug?
import fs from 'node:fs/promises';
const f = '/tmp/deleteme-utimes-issue-file';
await fs.writeFile(f, '');
console.log('|', 'set', '|', 'got', '|', 'got/set', '|');
console.log('|', '---', '|', '---', '|', '---', '|');
for (let i = 1; i < Number.MAX_SAFE_INTEGER; i *= 3) {
await fs.utimes(f, i, i);
const { mtimeMs } = await fs.stat(f);
console.log('|', i, '|', mtimeMs, '|', mtimeMs / i, '|');
}
await fs.rm(f);
How often does it reproduce? Is there a required condition?
Reproduces on one specific PC all the time. Couldn't reproduce on any other machine so far.
What is the expected behavior? Why is that the expected behavior?
Expected because 1 second is 1000 milliseconds
What do you see instead?
Additional information
This issue causes
test/parallel/test-fs-cp.mjs,test/parallel/test-fs-promises.js,test/parallel/test-fs-stat-bigint.js, andtest/parallel/test-fs-utimes-y2K38.jsfail for me locally every time. 😧This issue persists for
statSync(), too.This issue persists between different filesystems.
This issue does not appear when using
bigintoption.This issue does not appear when using
touch/stat, or similar functions in other languages.I suspect some weird bug with floating point.