Skip to content

Commit bea6488

Browse files
LiviaMedeirosaduh95
andcommitted
fs: harden fs.readSync(buffer, options) typecheck
Co-authored-by: Antoine du Hamel <[email protected]>
1 parent 920c2d5 commit bea6488

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
@@ -693,26 +693,28 @@ ObjectDefineProperty(read, internalUtil.customPromisifyArgs,
693693
* offset?: number;
694694
* length?: number;
695695
* position?: number | bigint | null;
696-
* }} [offset]
696+
* }} [offsetOrOptions]
697697
* @returns {number}
698698
*/
699-
function readSync(fd, buffer, offset, length, position) {
699+
function readSync(fd, buffer, offsetOrOptions, length, position) {
700700
fd = getValidatedFd(fd);
701701

702702
validateBuffer(buffer);
703703

704-
if (arguments.length <= 3) {
705-
// Assume fs.readSync(fd, buffer, options)
706-
const options = offset || ObjectCreate(null);
704+
let offset = offsetOrOptions;
705+
if (arguments.length <= 3 || typeof offsetOrOptions === 'object') {
706+
if (offsetOrOptions !== undefined) {
707+
validateObject(offsetOrOptions, 'options', { nullable: true });
708+
}
707709

708710
({
709711
offset = 0,
710712
length = buffer.byteLength - offset,
711713
position = null,
712-
} = options);
714+
} = offsetOrOptions);
713715
}
714716

715-
if (offset == null) {
717+
if (offset === undefined) {
716718
offset = 0;
717719
} else {
718720
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), options);
72+
runTest(Buffer.allocUnsafe(expected.length), options, 'ERR_INVALID_ARG_TYPE');
5773
}

0 commit comments

Comments
 (0)