Skip to content

Commit 6705b48

Browse files
anonrigrichardlau
authored andcommitted
fs: validate fd synchronously on c++
PR-URL: #51027 Reviewed-By: Matteo Collina <[email protected]>
1 parent 2a61602 commit 6705b48

File tree

8 files changed

+198
-60
lines changed

8 files changed

+198
-60
lines changed

lib/fs.js

+6-21
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,12 @@ function defaultCloseCallback(err) {
515515
* @returns {void}
516516
*/
517517
function close(fd, callback = defaultCloseCallback) {
518-
fd = getValidatedFd(fd);
519518
if (callback !== defaultCloseCallback)
520519
callback = makeCallback(callback);
521520

522521
const req = new FSReqCallback();
523522
req.oncomplete = callback;
524-
binding.close(fd, req);
523+
binding.close(getValidatedFd(fd), req);
525524
}
526525

527526
/**
@@ -530,9 +529,7 @@ function close(fd, callback = defaultCloseCallback) {
530529
* @returns {void}
531530
*/
532531
function closeSync(fd) {
533-
fd = getValidatedFd(fd);
534-
535-
binding.close(fd);
532+
binding.close(getValidatedFd(fd));
536533
}
537534

538535
/**
@@ -1106,7 +1103,6 @@ function ftruncate(fd, len = 0, callback) {
11061103
callback = len;
11071104
len = 0;
11081105
}
1109-
fd = getValidatedFd(fd);
11101106
validateInteger(len, 'len');
11111107
len = MathMax(0, len);
11121108
callback = makeCallback(callback);
@@ -1123,7 +1119,6 @@ function ftruncate(fd, len = 0, callback) {
11231119
* @returns {void}
11241120
*/
11251121
function ftruncateSync(fd, len = 0) {
1126-
fd = getValidatedFd(fd);
11271122
validateInteger(len, 'len');
11281123
len = MathMax(0, len);
11291124
binding.ftruncate(fd, len);
@@ -1275,7 +1270,6 @@ function rmSync(path, options) {
12751270
* @returns {void}
12761271
*/
12771272
function fdatasync(fd, callback) {
1278-
fd = getValidatedFd(fd);
12791273
const req = new FSReqCallback();
12801274
req.oncomplete = makeCallback(callback);
12811275
binding.fdatasync(fd, req);
@@ -1289,7 +1283,6 @@ function fdatasync(fd, callback) {
12891283
* @returns {void}
12901284
*/
12911285
function fdatasyncSync(fd) {
1292-
fd = getValidatedFd(fd);
12931286
binding.fdatasync(fd);
12941287
}
12951288

@@ -1301,7 +1294,6 @@ function fdatasyncSync(fd) {
13011294
* @returns {void}
13021295
*/
13031296
function fsync(fd, callback) {
1304-
fd = getValidatedFd(fd);
13051297
const req = new FSReqCallback();
13061298
req.oncomplete = makeCallback(callback);
13071299
binding.fsync(fd, req);
@@ -1314,7 +1306,6 @@ function fsync(fd, callback) {
13141306
* @returns {void}
13151307
*/
13161308
function fsyncSync(fd) {
1317-
fd = getValidatedFd(fd);
13181309
binding.fsync(fd);
13191310
}
13201311

@@ -1535,7 +1526,6 @@ function fstat(fd, options = { bigint: false }, callback) {
15351526
callback = options;
15361527
options = kEmptyObject;
15371528
}
1538-
fd = getValidatedFd(fd);
15391529
callback = makeStatsCallback(callback);
15401530

15411531
const req = new FSReqCallback(options.bigint);
@@ -1618,7 +1608,6 @@ function statfs(path, options = { bigint: false }, callback) {
16181608
* @returns {Stats | undefined}
16191609
*/
16201610
function fstatSync(fd, options = { bigint: false }) {
1621-
fd = getValidatedFd(fd);
16221611
const stats = binding.fstat(fd, options.bigint, undefined, false);
16231612
if (stats === undefined) {
16241613
return;
@@ -1884,7 +1873,6 @@ function unlinkSync(path) {
18841873
* @returns {void}
18851874
*/
18861875
function fchmod(fd, mode, callback) {
1887-
fd = getValidatedFd(fd);
18881876
mode = parseFileMode(mode, 'mode');
18891877
callback = makeCallback(callback);
18901878

@@ -1901,7 +1889,7 @@ function fchmod(fd, mode, callback) {
19011889
*/
19021890
function fchmodSync(fd, mode) {
19031891
binding.fchmod(
1904-
getValidatedFd(fd),
1892+
fd,
19051893
parseFileMode(mode, 'mode'),
19061894
);
19071895
}
@@ -2029,14 +2017,13 @@ function lchownSync(path, uid, gid) {
20292017
* @returns {void}
20302018
*/
20312019
function fchown(fd, uid, gid, callback) {
2032-
fd = getValidatedFd(fd);
20332020
validateInteger(uid, 'uid', -1, kMaxUserId);
20342021
validateInteger(gid, 'gid', -1, kMaxUserId);
20352022
callback = makeCallback(callback);
20362023

20372024
const req = new FSReqCallback();
20382025
req.oncomplete = callback;
2039-
binding.fchown(fd, uid, gid, req);
2026+
binding.fchown(getValidatedFd(fd), uid, gid, req);
20402027
}
20412028

20422029
/**
@@ -2047,11 +2034,10 @@ function fchown(fd, uid, gid, callback) {
20472034
* @returns {void}
20482035
*/
20492036
function fchownSync(fd, uid, gid) {
2050-
fd = getValidatedFd(fd);
20512037
validateInteger(uid, 'uid', -1, kMaxUserId);
20522038
validateInteger(gid, 'gid', -1, kMaxUserId);
20532039

2054-
binding.fchown(fd, uid, gid);
2040+
binding.fchown(getValidatedFd(fd), uid, gid);
20552041
}
20562042

20572043
/**
@@ -2141,7 +2127,6 @@ function utimesSync(path, atime, mtime) {
21412127
* @returns {void}
21422128
*/
21432129
function futimes(fd, atime, mtime, callback) {
2144-
fd = getValidatedFd(fd);
21452130
atime = toUnixTimestamp(atime, 'atime');
21462131
mtime = toUnixTimestamp(mtime, 'mtime');
21472132
callback = makeCallback(callback);
@@ -2162,7 +2147,7 @@ function futimes(fd, atime, mtime, callback) {
21622147
*/
21632148
function futimesSync(fd, atime, mtime) {
21642149
binding.futimes(
2165-
getValidatedFd(fd),
2150+
fd,
21662151
toUnixTimestamp(atime, 'atime'),
21672152
toUnixTimestamp(mtime, 'mtime'),
21682153
);

src/node_file.cc

+26-14
Original file line numberDiff line numberDiff line change
@@ -1242,8 +1242,10 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
12421242
const int argc = args.Length();
12431243
CHECK_GE(argc, 2);
12441244

1245-
CHECK(args[0]->IsInt32());
1246-
int fd = args[0].As<Int32>()->Value();
1245+
int fd;
1246+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
1247+
return;
1248+
}
12471249

12481250
bool use_bigint = args[1]->IsTrue();
12491251
if (!args[2]->IsUndefined()) { // fstat(fd, use_bigint, req)
@@ -1493,18 +1495,20 @@ static void FTruncate(const FunctionCallbackInfo<Value>& args) {
14931495
const int argc = args.Length();
14941496
CHECK_GE(argc, 2);
14951497

1496-
CHECK(args[0]->IsInt32());
1497-
const int fd = args[0].As<Int32>()->Value();
1498+
int fd;
1499+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
1500+
return;
1501+
}
14981502

14991503
CHECK(IsSafeJsInt(args[1]));
15001504
const int64_t len = args[1].As<Integer>()->Value();
15011505

1502-
if (argc > 2) {
1506+
if (argc > 2) { // ftruncate(fd, len, req)
15031507
FSReqBase* req_wrap_async = GetReqWrap(args, 2);
15041508
FS_ASYNC_TRACE_BEGIN0(UV_FS_FTRUNCATE, req_wrap_async)
15051509
AsyncCall(env, req_wrap_async, args, "ftruncate", UTF8, AfterNoArgs,
15061510
uv_fs_ftruncate, fd, len);
1507-
} else {
1511+
} else { // ftruncate(fd, len)
15081512
FSReqWrapSync req_wrap_sync("ftruncate");
15091513
FS_SYNC_TRACE_BEGIN(ftruncate);
15101514
SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_ftruncate, fd, len);
@@ -1518,8 +1522,10 @@ static void Fdatasync(const FunctionCallbackInfo<Value>& args) {
15181522
const int argc = args.Length();
15191523
CHECK_GE(argc, 1);
15201524

1521-
CHECK(args[0]->IsInt32());
1522-
const int fd = args[0].As<Int32>()->Value();
1525+
int fd;
1526+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
1527+
return;
1528+
}
15231529

15241530
if (argc > 1) { // fdatasync(fd, req)
15251531
FSReqBase* req_wrap_async = GetReqWrap(args, 1);
@@ -1541,8 +1547,10 @@ static void Fsync(const FunctionCallbackInfo<Value>& args) {
15411547
const int argc = args.Length();
15421548
CHECK_GE(argc, 1);
15431549

1544-
CHECK(args[0]->IsInt32());
1545-
const int fd = args[0].As<Int32>()->Value();
1550+
int fd;
1551+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
1552+
return;
1553+
}
15461554

15471555
if (argc > 1) {
15481556
FSReqBase* req_wrap_async = GetReqWrap(args, 1);
@@ -2615,8 +2623,10 @@ static void FChmod(const FunctionCallbackInfo<Value>& args) {
26152623
const int argc = args.Length();
26162624
CHECK_GE(argc, 2);
26172625

2618-
CHECK(args[0]->IsInt32());
2619-
const int fd = args[0].As<Int32>()->Value();
2626+
int fd;
2627+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
2628+
return;
2629+
}
26202630

26212631
CHECK(args[1]->IsInt32());
26222632
const int mode = args[1].As<Int32>()->Value();
@@ -2771,8 +2781,10 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
27712781
const int argc = args.Length();
27722782
CHECK_GE(argc, 3);
27732783

2774-
CHECK(args[0]->IsInt32());
2775-
const int fd = args[0].As<Int32>()->Value();
2784+
int fd;
2785+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
2786+
return;
2787+
}
27762788

27772789
CHECK(args[1]->IsNumber());
27782790
const double atime = args[1].As<Number>()->Value();

src/util.cc

+85
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

2222
#include "util.h" // NOLINT(build/include_inline)
23+
#include <cmath>
2324
#include "util-inl.h"
2425

2526
#include "debug_utils-inl.h"
@@ -31,6 +32,7 @@
3132
#include "node_v8_platform-inl.h"
3233
#include "string_bytes.h"
3334
#include "uv.h"
35+
#include "v8-value.h"
3436

3537
#ifdef _WIN32
3638
#include <io.h> // _S_IREAD _S_IWRITE
@@ -702,4 +704,87 @@ RAIIIsolate::RAIIIsolate(const SnapshotData* data)
702704

703705
RAIIIsolate::~RAIIIsolate() {}
704706

707+
// Returns a string representation of the input value, including type.
708+
// JavaScript implementation is available in lib/internal/errors.js
709+
std::string DetermineSpecificErrorType(Environment* env,
710+
v8::Local<v8::Value> input) {
711+
if (input->IsFunction()) {
712+
return "function";
713+
} else if (input->IsString()) {
714+
auto value = Utf8Value(env->isolate(), input).ToString();
715+
if (value.size() > 28) {
716+
value = value.substr(0, 25) + "...";
717+
}
718+
if (value.find('\'') == std::string::npos) {
719+
return SPrintF("type string ('%s')", value);
720+
}
721+
722+
// Stringify the input value.
723+
Local<String> stringified =
724+
v8::JSON::Stringify(env->context(), input).ToLocalChecked();
725+
Utf8Value stringified_value(env->isolate(), stringified);
726+
return SPrintF("type string (%s)", stringified_value.out());
727+
} else if (input->IsObject()) {
728+
v8::Local<v8::String> constructor_name =
729+
input.As<v8::Object>()->GetConstructorName();
730+
Utf8Value name(env->isolate(), constructor_name);
731+
return SPrintF("an instance of %s", name.out());
732+
}
733+
734+
Utf8Value utf8_value(env->isolate(),
735+
input->ToString(env->context()).ToLocalChecked());
736+
737+
if (input->IsNumber() || input->IsInt32() || input->IsUint32()) {
738+
auto value = input.As<v8::Number>()->Value();
739+
if (std::isnan(value)) {
740+
return "type number (NaN)";
741+
} else if (std::isinf(value)) {
742+
return "type number (Infinity)";
743+
}
744+
return SPrintF("type number (%s)", utf8_value.out());
745+
} else if (input->IsBigInt() || input->IsBoolean() || input->IsSymbol()) {
746+
Utf8Value type(env->isolate(), input->TypeOf(env->isolate()));
747+
return SPrintF("type %s (%s)", type.out(), utf8_value.out());
748+
}
749+
750+
// For example: null, undefined
751+
return utf8_value.ToString();
752+
}
753+
754+
v8::Maybe<int32_t> GetValidatedFd(Environment* env,
755+
v8::Local<v8::Value> input) {
756+
if (!input->IsInt32() && !input->IsNumber()) {
757+
std::string error_type = node::DetermineSpecificErrorType(env, input);
758+
THROW_ERR_INVALID_ARG_TYPE(env,
759+
"The \"fd\" argument must be of type "
760+
"number. Received %s",
761+
error_type.c_str());
762+
return v8::Nothing<int32_t>();
763+
}
764+
765+
const double fd = input.As<v8::Number>()->Value();
766+
const bool is_out_of_range = fd < 0 || fd > INT32_MAX;
767+
768+
if (is_out_of_range || !IsSafeJsInt(input)) {
769+
Utf8Value utf8_value(
770+
env->isolate(), input->ToDetailString(env->context()).ToLocalChecked());
771+
if (is_out_of_range && !std::isinf(fd)) {
772+
THROW_ERR_OUT_OF_RANGE(env,
773+
"The value of \"fd\" is out of range. "
774+
"It must be >= 0 && <= %s. Received %s",
775+
std::to_string(INT32_MAX),
776+
utf8_value.out());
777+
} else {
778+
THROW_ERR_OUT_OF_RANGE(
779+
env,
780+
"The value of \"fd\" is out of range. It must be an integer. "
781+
"Received %s",
782+
utf8_value.out());
783+
}
784+
return v8::Nothing<int32_t>();
785+
}
786+
787+
return v8::Just(static_cast<int32_t>(fd));
788+
}
789+
705790
} // namespace node

src/util.h

+5
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,11 @@ class RAIIIsolate {
10131013
v8::Isolate::Scope isolate_scope_;
10141014
};
10151015

1016+
std::string DetermineSpecificErrorType(Environment* env,
1017+
v8::Local<v8::Value> input);
1018+
1019+
v8::Maybe<int32_t> GetValidatedFd(Environment* env, v8::Local<v8::Value> input);
1020+
10161021
} // namespace node
10171022

10181023
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

0 commit comments

Comments
 (0)