Skip to content

Commit 23c6c66

Browse files
LiviaMedeirosaduh95
authored andcommitted
fs: harden fs.readSync(buffer, options) typecheck
Co-authored-by: Antoine du Hamel <[email protected]> PR-URL: nodejs#42772 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent a885995 commit 23c6c66

File tree

2 files changed

+34
-16
lines changed

2 files changed

+34
-16
lines changed

lib/fs.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -701,26 +701,28 @@ ObjectDefineProperty(read, kCustomPromisifyArgsSymbol,
701701
* offset?: number;
702702
* length?: number;
703703
* position?: number | bigint | null;
704-
* }} [offset]
704+
* }} [offsetOrOptions]
705705
* @returns {number}
706706
*/
707-
function readSync(fd, buffer, offset, length, position) {
707+
function readSync(fd, buffer, offsetOrOptions, length, position) {
708708
fd = getValidatedFd(fd);
709709

710710
validateBuffer(buffer);
711711

712-
if (arguments.length <= 3) {
713-
// Assume fs.readSync(fd, buffer, options)
714-
const options = offset || kEmptyObject;
712+
let offset = offsetOrOptions;
713+
if (arguments.length <= 3 || typeof offsetOrOptions === 'object') {
714+
if (offsetOrOptions !== undefined) {
715+
validateObject(offsetOrOptions, 'options', { nullable: true });
716+
}
715717

716718
({
717719
offset = 0,
718720
length = buffer.byteLength - offset,
719721
position = null,
720-
} = options);
722+
} = offsetOrOptions ?? kEmptyObject);
721723
}
722724

723-
if (offset == null) {
725+
if (offset === undefined) {
724726
offset = 0;
725727
} else {
726728
validateInteger(offset, 'offset', 0);

test/parallel/test-fs-readSync-optional-params.js

+25-9
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@ const filepath = fixtures.path('x.txt');
88

99
const expected = Buffer.from('xyz\n');
1010

11-
function runTest(defaultBuffer, options) {
11+
function runTest(defaultBuffer, options, errorCode = false) {
1212
let fd;
1313
try {
1414
fd = fs.openSync(filepath, 'r');
15-
const result = fs.readSync(fd, defaultBuffer, options);
16-
assert.strictEqual(result, expected.length);
17-
assert.deepStrictEqual(defaultBuffer, expected);
15+
if (errorCode) {
16+
assert.throws(
17+
() => fs.readSync(fd, defaultBuffer, options),
18+
{ code: errorCode }
19+
);
20+
} else {
21+
const result = fs.readSync(fd, defaultBuffer, options);
22+
assert.strictEqual(result, expected.length);
23+
assert.deepStrictEqual(defaultBuffer, expected);
24+
}
1825
} finally {
1926
if (fd != null) fs.closeSync(fd);
2027
}
@@ -31,7 +38,6 @@ for (const options of [
3138
{ length: expected.length, position: 0 },
3239
{ offset: 0, length: expected.length, position: 0 },
3340

34-
{ offset: null },
3541
{ position: null },
3642
{ position: -1 },
3743
{ position: 0n },
@@ -41,17 +47,27 @@ for (const options of [
4147
null,
4248
undefined,
4349

44-
// Test if bad params are interpreted as default (not mandatory)
50+
// Test malicious corner case: it works as {length: 4} but not intentionally
51+
new String('4444'),
52+
]) {
53+
runTest(Buffer.allocUnsafe(expected.length), options);
54+
}
55+
56+
for (const options of [
57+
58+
// Test various invalid options
4559
false,
4660
true,
4761
Infinity,
4862
42n,
4963
Symbol(),
64+
'amString',
65+
[],
66+
() => {},
5067

51-
// Test even more malicious corner cases
68+
// Test if arbitrary entity with expected .length is not mistaken for options
5269
'4'.repeat(expected.length),
53-
new String('4444'),
5470
[4, 4, 4, 4],
5571
]) {
56-
runTest(Buffer.allocUnsafe(expected.length), mustNotMutateObjectDeep(options));
72+
runTest(Buffer.allocUnsafe(expected.length), mustNotMutateObjectDeep(options), 'ERR_INVALID_ARG_TYPE');
5773
}

0 commit comments

Comments
 (0)