Skip to content

Commit 34667c0

Browse files
anonrigmarco-ippolito
authored andcommitted
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 a6473a8 commit 34667c0

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
@@ -107,7 +107,6 @@ const {
107107
getOptions,
108108
getValidatedFd,
109109
getValidatedPath,
110-
getValidMode,
111110
handleErrorFromBinding,
112111
preprocessSymlinkDestination,
113112
Stats,
@@ -232,7 +231,6 @@ function access(path, mode, callback) {
232231
}
233232

234233
path = getValidatedPath(path);
235-
mode = getValidMode(mode, 'access');
236234
callback = makeCallback(callback);
237235

238236
const req = new FSReqCallback();
@@ -249,8 +247,6 @@ function access(path, mode, callback) {
249247
*/
250248
function accessSync(path, mode) {
251249
path = getValidatedPath(path);
252-
mode = getValidMode(mode, 'access');
253-
254250
binding.access(pathModule.toNamespacedPath(path), mode);
255251
}
256252

@@ -2982,7 +2978,6 @@ function copyFile(src, dest, mode, callback) {
29822978

29832979
src = pathModule.toNamespacedPath(src);
29842980
dest = pathModule.toNamespacedPath(dest);
2985-
mode = getValidMode(mode, 'copyFile');
29862981
callback = makeCallback(callback);
29872982

29882983
const req = new FSReqCallback();
@@ -3005,7 +3000,7 @@ function copyFileSync(src, dest, mode) {
30053000
binding.copyFile(
30063001
pathModule.toNamespacedPath(src),
30073002
pathModule.toNamespacedPath(dest),
3008-
getValidMode(mode, 'copyFile'),
3003+
mode,
30093004
);
30103005
}
30113006

lib/internal/fs/promises.js

-3
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ const {
6060
getStatFsFromBinding,
6161
getStatsFromBinding,
6262
getValidatedPath,
63-
getValidMode,
6463
preprocessSymlinkDestination,
6564
stringToFlags,
6665
stringToSymlinkType,
@@ -598,7 +597,6 @@ async function readFileHandle(filehandle, options) {
598597
async function access(path, mode = F_OK) {
599598
path = getValidatedPath(path);
600599

601-
mode = getValidMode(mode, 'access');
602600
return await PromisePrototypeThen(
603601
binding.access(pathModule.toNamespacedPath(path), mode, kUsePromises),
604602
undefined,
@@ -616,7 +614,6 @@ async function cp(src, dest, options) {
616614
async function copyFile(src, dest, mode) {
617615
src = getValidatedPath(src, 'src');
618616
dest = getValidatedPath(dest, 'dest');
619-
mode = getValidMode(mode, 'copyFile');
620617
return await PromisePrototypeThen(
621618
binding.copyFile(pathModule.toNamespacedPath(src),
622619
pathModule.toNamespacedPath(dest),

lib/internal/fs/utils.js

-1
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,6 @@ module.exports = {
951951
getOptions,
952952
getValidatedFd,
953953
getValidatedPath,
954-
getValidMode,
955954
handleErrorFromBinding,
956955
possiblyTransformPath,
957956
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);
@@ -2079,7 +2082,12 @@ static void CopyFile(const FunctionCallbackInfo<Value>& args) {
20792082
Isolate* isolate = env->isolate();
20802083

20812084
const int argc = args.Length();
2082-
CHECK_GE(argc, 3);
2085+
CHECK_GE(argc, 3); // src, dest, flags
2086+
2087+
int flags;
2088+
if (!GetValidFileMode(env, args[2], UV_FS_COPYFILE).To(&flags)) {
2089+
return;
2090+
}
20832091

20842092
BufferValue src(isolate, args[0]);
20852093
CHECK_NOT_NULL(*src);
@@ -2091,9 +2099,6 @@ static void CopyFile(const FunctionCallbackInfo<Value>& args) {
20912099
THROW_IF_INSUFFICIENT_PERMISSIONS(
20922100
env, permission::PermissionScope::kFileSystemWrite, dest.ToStringView());
20932101

2094-
CHECK(args[2]->IsInt32());
2095-
const int flags = args[2].As<Int32>()->Value();
2096-
20972102
if (argc > 3) { // copyFile(src, dest, flags, req)
20982103
FSReqBase* req_wrap_async = GetReqWrap(args, 3);
20992104
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"
@@ -1017,6 +1018,9 @@ std::string DetermineSpecificErrorType(Environment* env,
10171018
v8::Local<v8::Value> input);
10181019

10191020
v8::Maybe<int32_t> GetValidatedFd(Environment* env, v8::Local<v8::Value> input);
1021+
v8::Maybe<int> GetValidFileMode(Environment* env,
1022+
v8::Local<v8::Value> input,
1023+
uv_fs_type type);
10201024

10211025
// Returns true if OS==Windows and filename ends in .bat or .cmd,
10221026
// case insensitive.

0 commit comments

Comments
 (0)