diff --git a/lib/fs.js b/lib/fs.js index 73365c60a04cde..e646ec4a7dcb76 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -454,7 +454,12 @@ function read(fd, buffer, offset, length, position, callback) { validateBuffer(buffer); callback = maybeCallback(callback); - offset |= 0; + if (offset == null) { + offset = 0; + } else { + validateSafeInteger(offset, 'offset'); + } + length |= 0; if (length === 0) { @@ -491,7 +496,12 @@ function readSync(fd, buffer, offset, length, position) { validateUint32(fd, 'fd'); validateBuffer(buffer); - offset |= 0; + if (offset == null) { + offset = 0; + } else { + validateSafeInteger(offset, 'offset'); + } + length |= 0; if (length === 0) { diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 35d08e0a8cae3f..d128e9e2a3ff44 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -208,7 +208,12 @@ async function read(handle, buffer, offset, length, position) { validateFileHandle(handle); validateBuffer(buffer); - offset |= 0; + if (offset == null) { + offset = 0; + } else { + validateSafeInteger(offset, 'offset'); + } + length |= 0; if (length === 0) diff --git a/src/node_file.cc b/src/node_file.cc index 95a239ee52bf96..ee8710fa5b7354 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -24,7 +24,7 @@ #include "node_buffer.h" #include "node_process.h" #include "node_stat_watcher.h" -#include "util.h" +#include "util-inl.h" #include "tracing/trace_event.h" @@ -1831,7 +1831,7 @@ static void WriteString(const FunctionCallbackInfo& args) { * * 0 fd int32. file descriptor * 1 buffer instance of Buffer - * 2 offset int32. offset to start reading into inside buffer + * 2 offset int64. offset to start reading into inside buffer * 3 length int32. length to read * 4 position int64. file position - -1 for current position */ @@ -1849,15 +1849,17 @@ static void Read(const FunctionCallbackInfo& args) { char* buffer_data = Buffer::Data(buffer_obj); size_t buffer_length = Buffer::Length(buffer_obj); - CHECK(args[2]->IsInt32()); - const size_t off = static_cast(args[2].As()->Value()); - CHECK_LT(off, buffer_length); + CHECK(IsSafeJsInt(args[2])); + const int64_t off_64 = args[2].As()->Value(); + CHECK_GE(off_64, 0); + CHECK_LT(static_cast(off_64), buffer_length); + const size_t off = static_cast(off_64); CHECK(args[3]->IsInt32()); const size_t len = static_cast(args[3].As()->Value()); CHECK(Buffer::IsWithinBounds(off, len, buffer_length)); - CHECK(args[4]->IsNumber()); + CHECK(IsSafeJsInt(args[4])); const int64_t pos = args[4].As()->Value(); char* buf = buffer_data + off; diff --git a/src/util-inl.h b/src/util-inl.h index 5b59f25bc5ff16..df73665977ace6 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -24,6 +24,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include #include "util.h" @@ -491,6 +492,17 @@ void ArrayBufferViewContents::Read(v8::Local abv) { } } +// ECMA262 20.1.2.5 +inline bool IsSafeJsInt(v8::Local v) { + if (!v->IsNumber()) return false; + double v_d = v.As()->Value(); + if (std::isnan(v_d)) return false; + if (std::isinf(v_d)) return false; + if (std::trunc(v_d) != v_d) return false; // not int + if (std::abs(v_d) <= static_cast(kMaxSafeJsInteger)) return true; + return false; +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/util.h b/src/util.h index 56b2bc83382698..a1ae4894a090b7 100644 --- a/src/util.h +++ b/src/util.h @@ -168,6 +168,10 @@ void DumpBacktrace(FILE* fp); #define UNREACHABLE() ABORT() +constexpr int64_t kMaxSafeJsInteger = 9007199254740991; // 2^53-1 + +inline bool IsSafeJsInt(v8::Local v); + // TAILQ-style intrusive list node. template class ListNode;