diff options
author | Zach Bjornson <zbbjornson@gmail.com> | 2019-03-18 11:27:13 -0700 |
---|---|---|
committer | Rich Trott <rtrott@gmail.com> | 2019-08-16 22:09:27 -0700 |
commit | 0bbda5e5aede9b264a3c6188529c9dbed1ec9719 (patch) | |
tree | d5b87a4fe8a7e9e12f91fc71f983c5d0a661c33d | |
parent | 91a4cb71753b7c7012022e3e67c7a1f16f3b5e80 (diff) | |
download | android-node-v8-0bbda5e5aede9b264a3c6188529c9dbed1ec9719.tar.gz android-node-v8-0bbda5e5aede9b264a3c6188529c9dbed1ec9719.tar.bz2 android-node-v8-0bbda5e5aede9b264a3c6188529c9dbed1ec9719.zip |
fs: allow int64 offset in fs.read/readSync/fd.read
Since v10.10.0, 'buf' can be any DataView, meaning the largest
byteLength can be Float64Array.BYTES_PER_ELEMENT * kMaxLength =
17,179,869,176.
'offset' can now be up to 2**53 - 1. This makes it possible to tile
reads into a large buffer.
Breaking: now throws if read offset is not a safe int, is null or
is undefined.
Fixes https://github.com/nodejs/node/issues/26563
PR-URL: https://github.com/nodejs/node/pull/26572
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
-rw-r--r-- | lib/fs.js | 14 | ||||
-rw-r--r-- | lib/internal/fs/promises.js | 7 | ||||
-rw-r--r-- | src/node_file.cc | 12 | ||||
-rw-r--r-- | src/util-inl.h | 12 | ||||
-rw-r--r-- | src/util.h | 5 | ||||
-rw-r--r-- | test/parallel/test-fs-read-type.js | 27 |
6 files changed, 69 insertions, 8 deletions
@@ -453,7 +453,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) { @@ -490,7 +495,12 @@ function readSync(fd, buffer, offset, length, position) { validateInt32(fd, 'fd', 0); 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 c8203055b5..67237c954b 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -206,7 +206,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 138848c49d..5bd0237e7b 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1851,7 +1851,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& 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 */ @@ -1869,15 +1869,17 @@ static void Read(const FunctionCallbackInfo<Value>& 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<size_t>(args[2].As<Int32>()->Value()); - CHECK_LT(off, buffer_length); + CHECK(IsSafeJsInt(args[2])); + const int64_t off_64 = args[2].As<Integer>()->Value(); + CHECK_GE(off_64, 0); + CHECK_LT(static_cast<uint64_t>(off_64), buffer_length); + const size_t off = static_cast<size_t>(off_64); CHECK(args[3]->IsInt32()); const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value()); CHECK(Buffer::IsWithinBounds(off, len, buffer_length)); - CHECK(args[4]->IsNumber()); + CHECK(IsSafeJsInt(args[4])); const int64_t pos = args[4].As<Integer>()->Value(); char* buf = buffer_data + off; diff --git a/src/util-inl.h b/src/util-inl.h index 5f8e7acb2a..c06a0ae84c 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -24,6 +24,7 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include <cmath> #include <cstring> #include "util.h" @@ -521,6 +522,17 @@ void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) { } } +// ECMA262 20.1.2.5 +inline bool IsSafeJsInt(v8::Local<v8::Value> v) { + if (!v->IsNumber()) return false; + double v_d = v.As<v8::Number>()->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<double>(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 a94e88f232..c8bb44721f 100644 --- a/src/util.h +++ b/src/util.h @@ -184,6 +184,11 @@ void DumpBacktrace(FILE* fp); #define UNREACHABLE(...) \ ERROR_AND_ABORT("Unreachable code reached" __VA_OPT__(": ") __VA_ARGS__) +// ECMA262 20.1.2.6 Number.MAX_SAFE_INTEGER (2^53-1) +constexpr int64_t kMaxSafeJsInteger = 9007199254740991; + +inline bool IsSafeJsInt(v8::Local<v8::Value> v); + // TAILQ-style intrusive list node. template <typename T> class ListNode; diff --git a/test/parallel/test-fs-read-type.js b/test/parallel/test-fs-read-type.js index b51df51589..f5ac78a230 100644 --- a/test/parallel/test-fs-read-type.js +++ b/test/parallel/test-fs-read-type.js @@ -53,6 +53,20 @@ assert.throws(() => { assert.throws(() => { fs.read(fd, Buffer.allocUnsafe(expected.length), + NaN, + expected.length, + 0, + common.mustNotCall()); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. It must be an integer. ' + + 'Received NaN' +}); + +assert.throws(() => { + fs.read(fd, + Buffer.allocUnsafe(expected.length), 0, -1, 0, @@ -106,6 +120,19 @@ assert.throws(() => { assert.throws(() => { fs.readSync(fd, Buffer.allocUnsafe(expected.length), + NaN, + expected.length, + 0); +}, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. It must be an integer. ' + + 'Received NaN' +}); + +assert.throws(() => { + fs.readSync(fd, + Buffer.allocUnsafe(expected.length), 0, -1, 0); |