Commit 1e17275dea7 for nodejs
commit 1e17275dea7a1af3b23681bfdf9393d9416dc9dc
Author: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
Date: Sat Sep 26 16:52:53 2026 -0300
fs: fix crash on negative zero file descriptor
`isInt32()` accepts -0 because `-0 === (-0 | 0)`, but V8 does not
represent -0 as an Int32 value, so `Value::IsInt32()` rejects it. The
utf8 fast paths of `readFileSync()` and `writeFileSync()` hand the value
straight to the binding, which then took it for a path and aborted on
the null check.
Coerce -0 to 0 before the call, matching `getValidatedFd()` and the rest
of fs, where -0 is a valid way to name file descriptor 0.
Signed-off-by: Christian Aurich <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65888
Fixes: https://github.com/nodejs/node/issues/65886
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
diff --git a/lib/fs.js b/lib/fs.js
index 85fdd089060..4436fa2df6e 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -613,7 +613,11 @@ function readFileSync(path, options) {
if ((options.encoding === 'utf8' || options.encoding === 'utf-8') &&
!hasUserBuffer) {
- if (!isInt32(path)) {
+ if (isInt32(path)) {
+ // V8 does not report -0 as an int32, so it would reach the binding as a
+ // path instead of a file descriptor.
+ path |= 0;
+ } else {
path = getValidatedPath(path);
}
return binding.readFileUtf8(path, stringToFlags(options.flag));
@@ -3012,7 +3016,11 @@ function writeFileSync(path, data, options) {
// C++ fast path for string data and UTF8 encoding
if (typeof data === 'string' && (options.encoding === 'utf8' || options.encoding === 'utf-8')) {
- if (!isInt32(path)) {
+ if (isInt32(path)) {
+ // V8 does not report -0 as an int32, so it would reach the binding as a
+ // path instead of a file descriptor.
+ path |= 0;
+ } else {
path = getValidatedPath(path);
}
diff --git a/test/parallel/test-fs-negative-zero.js b/test/parallel/test-fs-negative-zero.js
index 538cea67faa..09f965eb171 100644
--- a/test/parallel/test-fs-negative-zero.js
+++ b/test/parallel/test-fs-negative-zero.js
@@ -2,6 +2,8 @@
require('../common');
+const assert = require('assert');
+const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
@@ -29,5 +31,18 @@ ignoreExpectedError(() => fs.mkdirSync(missing, { mode: -0 }));
ignoreExpectedError(() => fs.chmodSync(missing, -0));
ignoreExpectedError(() => fs.writeFileSync(missing, '', { mode: -0 }));
+// -0 is accepted as file descriptor 0. Writing an empty string reaches the
+// utf8 fast path without issuing a write on the descriptor.
+fs.writeFileSync(-0, '');
+fs.appendFileSync(-0, '');
+
+const child = spawnSync(
+ process.execPath,
+ ['-e', 'process.stdout.write(require("fs").readFileSync(-0, "utf8"))'],
+ { input: 'hello' },
+);
+assert.strictEqual(child.status, 0);
+assert.strictEqual(child.stdout.toString(), 'hello');
+
fs.watchFile(missing, { interval: -0 }, () => {});
fs.unwatchFile(missing);