From 35d0848e0cb2919817661b6e0b9ed93cc3f4746e Mon Sep 17 00:00:00 2001 From: yohannesjoya Date: Sun, 1 Nov 2026 14:02:52 +0300 Subject: [PATCH] fs: validate opendir() bufferSize before opening the directory fs.opendir() only checked options.bufferSize in the Dir constructor, which runs from the completion callback of the native opendir call. An invalid value was thrown from there, so it could not be caught and the process exited. fs.promises.opendir() is built on fs.opendir() and had the same problem. Check bufferSize before starting the native call, so fs.opendir() throws to the caller and fs.promises.opendir() rejects. Fixes: https://github.com/nodejs/node/issues/67182 Signed-off-by: yohannesjoya --- lib/internal/fs/dir.js | 7 ++++ .../test-fs-opendir-invalid-buffersize.js | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 test/parallel/test-fs-opendir-invalid-buffersize.js diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js index 32050f31ae6d..9c3963d33d92 100644 --- a/lib/internal/fs/dir.js +++ b/lib/internal/fs/dir.js @@ -5,6 +5,7 @@ const { ArrayPrototypeShift, FunctionPrototypeBind, ObjectDefineProperties, + ObjectHasOwn, PromiseReject, SymbolAsyncDispose, SymbolAsyncIterator, @@ -350,6 +351,12 @@ function opendir(path, options, callback) { encoding: 'utf8', }); + // Validate bufferSize before opening the directory. The Dir constructor + // validates it too, but an error thrown from opendirCallback can't be caught. + if (ObjectHasOwn(options, 'bufferSize')) { + validateUint32(options.bufferSize, 'options.bufferSize', true); + } + function opendirCallback(error, handle) { if (error) { callback(error); diff --git a/test/parallel/test-fs-opendir-invalid-buffersize.js b/test/parallel/test-fs-opendir-invalid-buffersize.js new file mode 100644 index 000000000000..53ab5bff5f9d --- /dev/null +++ b/test/parallel/test-fs-opendir-invalid-buffersize.js @@ -0,0 +1,36 @@ +'use strict'; + +// This tests that fs.opendir() and fs.promises.opendir() report an invalid +// bufferSize to the caller instead of throwing from an internal callback. + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +for (const bufferSize of [-1, 0, 0.5, 1.5, Infinity, NaN]) { + const expected = { code: 'ERR_OUT_OF_RANGE', name: 'RangeError' }; + assert.throws( + () => fs.opendir(tmpdir.path, { bufferSize }, common.mustNotCall()), + expected, + ); + assert.rejects(fs.promises.opendir(tmpdir.path, { bufferSize }), expected) + .then(common.mustCall()); +} + +for (const bufferSize of ['', '1', null, undefined]) { + const expected = { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' }; + assert.throws( + () => fs.opendir(tmpdir.path, { bufferSize }, common.mustNotCall()), + expected, + ); + assert.rejects(fs.promises.opendir(tmpdir.path, { bufferSize }), expected) + .then(common.mustCall()); +} + +// A valid bufferSize still opens the directory. +fs.opendir(tmpdir.path, { bufferSize: 1 }, common.mustSucceed((dir) => { + dir.closeSync(); +}));