Skip to content

Commit 693f14e

Browse files
committed
fs: do basic arg validation of truncate in js
This patch moves the basic validation of arguments to `truncate` family of functions to the JavaScript from the C++ layer.
1 parent 9359de9 commit 693f14e

6 files changed

+301
-118
lines changed

lib/fs.js

+70-37
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ function throwOptionsError(options) {
8181
'but got ' + typeof options + ' instead');
8282
}
8383

84+
function isFD(fd) {
85+
return Number.isInteger(fd) && fd >= 0 && fd <= 0xFFFFFFFF;
86+
}
87+
88+
function sanitizeFD(fd) {
89+
if (!isFD(fd))
90+
throw new TypeError('file descriptor must be a unsigned 32-bit integer');
91+
return fd;
92+
}
93+
8494
// Ensure that callbacks run in the global context. Only use this function
8595
// for callbacks that are passed to the binding layer, callbacks that are
8696
// invoked from JS already run in the proper scope.
@@ -112,10 +122,6 @@ function nullCheck(path, callback) {
112122
return true;
113123
}
114124

115-
function isFd(path) {
116-
return (path >>> 0) === path;
117-
}
118-
119125
// Static method to set the stats properties on a Stats object.
120126
fs.Stats = function(
121127
dev,
@@ -257,7 +263,7 @@ fs.readFile = function(path, options, callback) {
257263
return;
258264

259265
var context = new ReadFileContext(callback, encoding);
260-
context.isUserFd = isFd(path); // file descriptor ownership
266+
context.isUserFd = isFD(path); // file descriptor ownership
261267
var req = new FSReqWrap();
262268
req.context = context;
263269
req.oncomplete = readFileAfterOpen;
@@ -473,7 +479,7 @@ fs.readFileSync = function(path, options) {
473479
assertEncoding(encoding);
474480

475481
var flag = options.flag || 'r';
476-
var isUserFd = isFd(path); // file descriptor ownership
482+
var isUserFd = isFD(path); // file descriptor ownership
477483
var fd = isUserFd ? path : fs.openSync(path, flag, 0o666);
478484

479485
var st = tryStatSync(fd, isUserFd);
@@ -570,12 +576,12 @@ Object.defineProperty(exports, '_stringToFlags', {
570576

571577
fs.close = function(fd, callback) {
572578
var req = new FSReqWrap();
573-
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
574-
binding.close(fd, req);
579+
req.oncomplete = makeCallback(callback);
580+
binding.close(sanitizeFD(fd), req);
575581
};
576582

577583
fs.closeSync = function(fd) {
578-
return binding.close(fd);
584+
return binding.close(sanitizeFD(fd));
579585
};
580586

581587
function modeNum(m, def) {
@@ -612,6 +618,7 @@ fs.openSync = function(path, flags, mode) {
612618
var readWarned = false;
613619
fs.read = function(fd, buffer, offset, length, position, callback) {
614620
callback = makeCallback(arguments[arguments.length - 1]);
621+
fd = sanitizeFD(fd);
615622
if (!(buffer instanceof Buffer)) {
616623
// legacy string interface (fd, length, position, encoding, callback)
617624
readWarned = printDeprecation('fs.read\'s legacy String interface ' +
@@ -672,6 +679,7 @@ fs.readSync = function(fd, buffer, offset, length, position) {
672679
var legacy = false;
673680
var encoding;
674681

682+
fd = sanitizeFD(fd);
675683
if (!(buffer instanceof Buffer)) {
676684
// legacy string interface (fd, length, position, encoding, callback)
677685
readSyncWarned = printDeprecation('fs.readSync\'s legacy String interface' +
@@ -713,6 +721,7 @@ fs.readSync = function(fd, buffer, offset, length, position) {
713721
// fs.write(fd, string[, position[, encoding]], callback);
714722
fs.write = function(fd, buffer, offset, length, position, callback) {
715723
callback = makeCallback(arguments[arguments.length - 1]);
724+
fd = sanitizeFD(fd);
716725
function wrapper(err, written) {
717726
// Retain a reference to buffer so that it can't be GC'ed too soon.
718727
callback(err, written || 0, buffer);
@@ -748,6 +757,7 @@ fs.write = function(fd, buffer, offset, length, position, callback) {
748757
// OR
749758
// fs.writeSync(fd, string[, position[, encoding]]);
750759
fs.writeSync = function(fd, buffer, offset, length, position) {
760+
fd = sanitizeFD(fd);
751761
if (buffer instanceof Buffer) {
752762
if (position === undefined)
753763
position = null;
@@ -779,14 +789,18 @@ fs.renameSync = function(oldPath, newPath) {
779789
};
780790

781791
fs.truncate = function(path, len, callback) {
782-
if (typeof path === 'number') {
792+
callback = makeCallback(arguments[arguments.length - 1]);
793+
794+
if (isFD(path))
783795
return fs.ftruncate(path, len, callback);
784-
}
785796

786-
callback = makeCallback(arguments[arguments.length - 1]);
797+
if (typeof path !== 'string')
798+
throw new TypeError('path must be a string');
787799

788-
if (typeof len === 'function' || len === undefined) {
800+
if (typeof len === 'function' || len == undefined) {
789801
len = 0;
802+
} else if (!Number.isInteger(len) || len < 0) {
803+
throw new TypeError('length must be a positive integer');
790804
}
791805

792806
fs.open(path, 'r+', function(er, fd) {
@@ -802,13 +816,18 @@ fs.truncate = function(path, len, callback) {
802816
};
803817

804818
fs.truncateSync = function(path, len) {
805-
if (typeof path === 'number') {
806-
// legacy
819+
if (isFD(path))
807820
return fs.ftruncateSync(path, len);
808-
}
809-
if (len === undefined) {
821+
822+
if (typeof path !== 'string')
823+
throw new TypeError('path must be a string');
824+
825+
if (len === undefined || len === null) {
810826
len = 0;
827+
} else if (!Number.isInteger(len) || len < 0) {
828+
throw new TypeError('length must be a positive integer');
811829
}
830+
812831
// allow error to be thrown, but still close fd.
813832
var fd = fs.openSync(path, 'r+');
814833
var ret;
@@ -822,18 +841,30 @@ fs.truncateSync = function(path, len) {
822841
};
823842

824843
fs.ftruncate = function(fd, len, callback) {
825-
if (typeof len === 'function' || len === undefined) {
844+
callback = makeCallback(arguments[arguments.length - 1]);
845+
846+
fd = sanitizeFD(fd);
847+
848+
if (typeof len === 'function' || len == undefined) {
826849
len = 0;
850+
} else if (!Number.isInteger(len) || len < 0) {
851+
throw new TypeError('length must be a positive integer');
827852
}
853+
828854
var req = new FSReqWrap();
829-
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
855+
req.oncomplete = callback;
830856
binding.ftruncate(fd, len, req);
831857
};
832858

833859
fs.ftruncateSync = function(fd, len) {
834-
if (len === undefined) {
860+
fd = sanitizeFD(fd);
861+
862+
if (len === undefined || len === null) {
835863
len = 0;
864+
} else if (!Number.isInteger(len) || len < 0) {
865+
throw new TypeError('length must be a positive integer');
836866
}
867+
837868
return binding.ftruncate(fd, len);
838869
};
839870

@@ -853,21 +884,21 @@ fs.rmdirSync = function(path) {
853884
fs.fdatasync = function(fd, callback) {
854885
var req = new FSReqWrap();
855886
req.oncomplete = makeCallback(callback);
856-
binding.fdatasync(fd, req);
887+
binding.fdatasync(sanitizeFD(fd), req);
857888
};
858889

859890
fs.fdatasyncSync = function(fd) {
860-
return binding.fdatasync(fd);
891+
return binding.fdatasync(sanitizeFD(fd));
861892
};
862893

863894
fs.fsync = function(fd, callback) {
864895
var req = new FSReqWrap();
865-
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
866-
binding.fsync(fd, req);
896+
req.oncomplete = makeCallback(callback);
897+
binding.fsync(sanitizeFD(fd), req);
867898
};
868899

869900
fs.fsyncSync = function(fd) {
870-
return binding.fsync(fd);
901+
return binding.fsync(sanitizeFD(fd));
871902
};
872903

873904
fs.mkdir = function(path, mode, callback) {
@@ -915,8 +946,8 @@ fs.readdirSync = function(path, options) {
915946

916947
fs.fstat = function(fd, callback) {
917948
var req = new FSReqWrap();
918-
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
919-
binding.fstat(fd, req);
949+
req.oncomplete = makeCallback(callback);
950+
binding.fstat(sanitizeFD(fd), req);
920951
};
921952

922953
fs.lstat = function(path, callback) {
@@ -936,7 +967,7 @@ fs.stat = function(path, callback) {
936967
};
937968

938969
fs.fstatSync = function(fd) {
939-
return binding.fstat(fd);
970+
return binding.fstat(sanitizeFD(fd));
940971
};
941972

942973
fs.lstatSync = function(path) {
@@ -1053,11 +1084,11 @@ fs.unlinkSync = function(path) {
10531084
fs.fchmod = function(fd, mode, callback) {
10541085
var req = new FSReqWrap();
10551086
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
1056-
binding.fchmod(fd, modeNum(mode), req);
1087+
binding.fchmod(sanitizeFD(fd), modeNum(mode), req);
10571088
};
10581089

10591090
fs.fchmodSync = function(fd, mode) {
1060-
return binding.fchmod(fd, modeNum(mode));
1091+
return binding.fchmod(sanitizeFD(fd), modeNum(mode));
10611092
};
10621093

10631094
if (constants.hasOwnProperty('O_SYMLINK')) {
@@ -1136,11 +1167,11 @@ if (constants.hasOwnProperty('O_SYMLINK')) {
11361167
fs.fchown = function(fd, uid, gid, callback) {
11371168
var req = new FSReqWrap();
11381169
req.oncomplete = makeCallback(arguments[arguments.length - 1]);
1139-
binding.fchown(fd, uid, gid, req);
1170+
binding.fchown(sanitizeFD(fd), uid, gid, req);
11401171
};
11411172

11421173
fs.fchownSync = function(fd, uid, gid) {
1143-
return binding.fchown(fd, uid, gid);
1174+
return binding.fchown(sanitizeFD(fd), uid, gid);
11441175
};
11451176

11461177
fs.chown = function(path, uid, gid, callback) {
@@ -1197,6 +1228,7 @@ fs.utimesSync = function(path, atime, mtime) {
11971228

11981229
fs.futimes = function(fd, atime, mtime, callback) {
11991230
callback = makeCallback(arguments[arguments.length - 1]);
1231+
fd = sanitizeFD(fd);
12001232
atime = toUnixTimestamp(atime);
12011233
mtime = toUnixTimestamp(mtime);
12021234
var req = new FSReqWrap();
@@ -1205,6 +1237,7 @@ fs.futimes = function(fd, atime, mtime, callback) {
12051237
};
12061238

12071239
fs.futimesSync = function(fd, atime, mtime) {
1240+
fd = sanitizeFD(fd);
12081241
atime = toUnixTimestamp(atime);
12091242
mtime = toUnixTimestamp(mtime);
12101243
binding.futimes(fd, atime, mtime);
@@ -1257,7 +1290,7 @@ fs.writeFile = function(path, data, options, callback) {
12571290

12581291
var flag = options.flag || 'w';
12591292

1260-
if (isFd(path)) {
1293+
if (isFD(path)) {
12611294
writeFd(path, true);
12621295
return;
12631296
}
@@ -1291,7 +1324,7 @@ fs.writeFileSync = function(path, data, options) {
12911324
assertEncoding(options.encoding);
12921325

12931326
var flag = options.flag || 'w';
1294-
var isUserFd = isFd(path); // file descriptor ownership
1327+
var isUserFd = isFD(path); // file descriptor ownership
12951328
var fd = isUserFd ? path : fs.openSync(path, flag, options.mode);
12961329

12971330
if (!(data instanceof Buffer)) {
@@ -1329,7 +1362,7 @@ fs.appendFile = function(path, data, options, callback) {
13291362
options = util._extend({ flag: 'a' }, options);
13301363

13311364
// force append behavior when using a supplied file descriptor
1332-
if (isFd(path))
1365+
if (isFD(path))
13331366
options.flag = 'a';
13341367

13351368
fs.writeFile(path, data, options, callback);
@@ -1348,7 +1381,7 @@ fs.appendFileSync = function(path, data, options) {
13481381
options = util._extend({ flag: 'a' }, options);
13491382

13501383
// force append behavior when using a supplied file descriptor
1351-
if (isFd(path))
1384+
if (isFD(path))
13521385
options.flag = 'a';
13531386

13541387
fs.writeFileSync(path, data, options);
@@ -1932,7 +1965,7 @@ function SyncWriteStream(fd, options) {
19321965

19331966
options = options || {};
19341967

1935-
this.fd = fd;
1968+
this.fd = sanitizeFD(fd);
19361969
this.writable = true;
19371970
this.readable = false;
19381971
this.autoClose = options.autoClose === undefined ? true : options.autoClose;

0 commit comments

Comments
 (0)