Skip to content

Commit 3ec20f2

Browse files
authored
fs: validate file mode from cpp
PR-URL: #52050 Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 1abff07 commit 3ec20f2

File tree

6 files changed

+88
-18
lines changed

6 files changed

+88
-18
lines changed

lib/fs.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ const {
108108
getOptions,
109109
getValidatedFd,
110110
getValidatedPath,
111-
getValidMode,
112111
handleErrorFromBinding,
113112
preprocessSymlinkDestination,
114113
Stats,
@@ -233,7 +232,6 @@ function access(path, mode, callback) {
233232
}
234233

235234
path = getValidatedPath(path);
236-
mode = getValidMode(mode, 'access');
237235
callback = makeCallback(callback);
238236

239237
const req = new FSReqCallback();
@@ -250,8 +248,6 @@ function access(path, mode, callback) {
250248
*/
251249
function accessSync(path, mode) {
252250
path = getValidatedPath(path);
253-
mode = getValidMode(mode, 'access');
254-
255251
binding.access(pathModule.toNamespacedPath(path), mode);
256252
}
257253

@@ -2984,7 +2980,6 @@ function copyFile(src, dest, mode, callback) {
29842980

29852981
src = pathModule.toNamespacedPath(src);
29862982
dest = pathModule.toNamespacedPath(dest);
2987-
mode = getValidMode(mode, 'copyFile');
29882983
callback = makeCallback(callback);
29892984

29902985
const req = new FSReqCallback();
@@ -3007,7 +3002,7 @@ function copyFileSync(src, dest, mode) {
30073002
binding.copyFile(
30083003
pathModule.toNamespacedPath(src),
30093004
pathModule.toNamespacedPath(dest),
3010-
getValidMode(mode, 'copyFile'),
3005+
mode,
30113006
);
30123007
}
30133008

lib/internal/fs/promises.js

-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ const {
5959
getStatFsFromBinding,
6060
getStatsFromBinding,
6161
getValidatedPath,
62-
getValidMode,
6362
preprocessSymlinkDestination,
6463
stringToFlags,
6564
stringToSymlinkType,
@@ -600,7 +599,6 @@ async function readFileHandle(filehandle, options) {
600599
async function access(path, mode = F_OK) {
601600
path = getValidatedPath(path);
602601

603-
mode = getValidMode(mode, 'access');
604602
return await PromisePrototypeThen(
605603
binding.access(pathModule.toNamespacedPath(path), mode, kUsePromises),
606604
undefined,
@@ -618,7 +616,6 @@ async function cp(src, dest, options) {
618616
async function copyFile(src, dest, mode) {
619617
src = getValidatedPath(src, 'src');
620618
dest = getValidatedPath(dest, 'dest');
621-
mode = getValidMode(mode, 'copyFile');
622619
return await PromisePrototypeThen(
623620
binding.copyFile(pathModule.toNamespacedPath(src),
624621
pathModule.toNamespacedPath(dest),

lib/internal/fs/utils.js

-1
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,6 @@ module.exports = {
10001000
getOptions,
10011001
getValidatedFd,
10021002
getValidatedPath,
1003-
getValidMode,
10041003
handleErrorFromBinding,
10051004
possiblyTransformPath,
10061005
preprocessSymlinkDestination,

src/node_file.cc

+12-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "req_wrap-inl.h"
3939
#include "stream_base-inl.h"
4040
#include "string_bytes.h"
41+
#include "uv.h"
4142

4243
#if defined(__MINGW32__) || defined(_MSC_VER)
4344
# include <io.h>
@@ -950,10 +951,12 @@ void Access(const FunctionCallbackInfo<Value>& args) {
950951
HandleScope scope(isolate);
951952

952953
const int argc = args.Length();
953-
CHECK_GE(argc, 2);
954+
CHECK_GE(argc, 2); // path, mode
954955

955-
CHECK(args[1]->IsInt32());
956-
int mode = args[1].As<Int32>()->Value();
956+
int mode;
957+
if (!GetValidFileMode(env, args[1], UV_FS_ACCESS).To(&mode)) {
958+
return;
959+
}
957960

958961
BufferValue path(isolate, args[0]);
959962
CHECK_NOT_NULL(*path);
@@ -1982,7 +1985,12 @@ static void CopyFile(const FunctionCallbackInfo<Value>& args) {
19821985
Isolate* isolate = env->isolate();
19831986

19841987
const int argc = args.Length();
1985-
CHECK_GE(argc, 3);
1988+
CHECK_GE(argc, 3); // src, dest, flags
1989+
1990+
int flags;
1991+
if (!GetValidFileMode(env, args[2], UV_FS_COPYFILE).To(&flags)) {
1992+
return;
1993+
}
19861994

19871995
BufferValue src(isolate, args[0]);
19881996
CHECK_NOT_NULL(*src);
@@ -1994,9 +2002,6 @@ static void CopyFile(const FunctionCallbackInfo<Value>& args) {
19942002
THROW_IF_INSUFFICIENT_PERMISSIONS(
19952003
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
19962004

1997-
CHECK(args[2]->IsInt32());
1998-
const int flags = args[2].As<Int32>()->Value();
1999-
20002005
if (argc > 3) { // copyFile(src, dest, flags, req)
20012006
FSReqBase* req_wrap_async = GetReqWrap(args, 3);
20022007
FS_ASYNC_TRACE_BEGIN2(UV_FS_COPYFILE,

src/util.cc

+71-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include "util.h" // NOLINT(build/include_inline)
2323
#include <cmath>
24+
#include <cstdint>
2425
#include "util-inl.h"
2526

2627
#include "debug_utils-inl.h"
@@ -31,7 +32,6 @@
3132
#include "node_snapshot_builder.h"
3233
#include "node_v8_platform-inl.h"
3334
#include "string_bytes.h"
34-
#include "uv.h"
3535
#include "v8-value.h"
3636

3737
#ifdef _WIN32
@@ -56,6 +56,31 @@
5656

5757
static std::atomic_int seq = {0}; // Sequence number for diagnostic filenames.
5858

59+
// F_OK etc. constants
60+
#ifdef _WIN32
61+
#include "uv.h"
62+
#else
63+
#include <unistd.h>
64+
#endif
65+
66+
// The access modes can be any of F_OK, R_OK, W_OK or X_OK. Some might not be
67+
// available on specific systems. They can be used in combination as well
68+
// (F_OK | R_OK | W_OK | X_OK).
69+
constexpr int kMaximumAccessMode = F_OK | W_OK | R_OK | X_OK;
70+
constexpr int kMinimumAccessMode = std::min({F_OK, W_OK, R_OK, X_OK});
71+
72+
constexpr int kDefaultCopyMode = 0;
73+
// The copy modes can be any of UV_FS_COPYFILE_EXCL, UV_FS_COPYFILE_FICLONE or
74+
// UV_FS_COPYFILE_FICLONE_FORCE. They can be used in combination as well
75+
// (US_FS_COPYFILE_EXCL | US_FS_COPYFILE_FICLONE |
76+
// US_FS_COPYFILE_FICLONE_FORCE).
77+
constexpr int kMinimumCopyMode = std::min({kDefaultCopyMode,
78+
UV_FS_COPYFILE_EXCL,
79+
UV_FS_COPYFILE_FICLONE,
80+
UV_FS_COPYFILE_FICLONE_FORCE});
81+
constexpr int kMaximumCopyMode =
82+
UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE | UV_FS_COPYFILE_FICLONE_FORCE;
83+
5984
namespace node {
6085

6186
using v8::ArrayBuffer;
@@ -787,4 +812,49 @@ v8::Maybe<int32_t> GetValidatedFd(Environment* env,
787812
return v8::Just(static_cast<int32_t>(fd));
788813
}
789814

815+
v8::Maybe<int> GetValidFileMode(Environment* env,
816+
v8::Local<v8::Value> input,
817+
uv_fs_type type) {
818+
// Allow only int32 or null/undefined values.
819+
if (input->IsNumber()) {
820+
// We cast the input to v8::Number to avoid overflows.
821+
auto num = input.As<v8::Number>()->Value();
822+
823+
// Handle infinity and NaN values
824+
if (std::isinf(num) || std::isnan(num)) {
825+
THROW_ERR_OUT_OF_RANGE(env, "mode is out of range");
826+
return v8::Nothing<int>();
827+
}
828+
} else if (!input->IsNullOrUndefined()) {
829+
THROW_ERR_INVALID_ARG_TYPE(env, "mode must be int32 or null/undefined");
830+
return v8::Nothing<int>();
831+
}
832+
833+
int min = kMinimumAccessMode;
834+
int max = kMaximumAccessMode;
835+
int def = F_OK;
836+
837+
CHECK(type == UV_FS_ACCESS || type == UV_FS_COPYFILE);
838+
839+
if (type == UV_FS_COPYFILE) {
840+
min = kMinimumCopyMode;
841+
max = kMaximumCopyMode;
842+
def = input->IsNullOrUndefined() ? kDefaultCopyMode
843+
: input.As<v8::Int32>()->Value();
844+
}
845+
846+
if (input->IsNullOrUndefined()) {
847+
return v8::Just(def);
848+
}
849+
850+
const int mode = input.As<v8::Int32>()->Value();
851+
if (mode < min || mode > max) {
852+
THROW_ERR_OUT_OF_RANGE(
853+
env, "mode is out of range: >= %d && <= %d", min, max);
854+
return v8::Nothing<int>();
855+
}
856+
857+
return v8::Just(mode);
858+
}
859+
790860
} // namespace node

src/util.h

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
2626

27+
#include "uv.h"
2728
#include "v8.h"
2829

2930
#include "node.h"
@@ -1027,6 +1028,9 @@ std::string DetermineSpecificErrorType(Environment* env,
10271028
v8::Local<v8::Value> input);
10281029

10291030
v8::Maybe<int32_t> GetValidatedFd(Environment* env, v8::Local<v8::Value> input);
1031+
v8::Maybe<int> GetValidFileMode(Environment* env,
1032+
v8::Local<v8::Value> input,
1033+
uv_fs_type type);
10301034

10311035
} // namespace node
10321036

0 commit comments

Comments
 (0)