Skip to content

Commit de27e44

Browse files
committed
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. Ref #26563
1 parent 006d530 commit de27e44

File tree

5 files changed

+42
-9
lines changed

5 files changed

+42
-9
lines changed

lib/fs.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,12 @@ function read(fd, buffer, offset, length, position, callback) {
454454
validateBuffer(buffer);
455455
callback = maybeCallback(callback);
456456

457-
offset |= 0;
457+
if (offset == null) {
458+
offset = 0;
459+
} else {
460+
validateSafeInteger(offset, 'offset');
461+
}
462+
458463
length |= 0;
459464

460465
if (length === 0) {
@@ -491,7 +496,12 @@ function readSync(fd, buffer, offset, length, position) {
491496
validateUint32(fd, 'fd');
492497
validateBuffer(buffer);
493498

494-
offset |= 0;
499+
if (offset == null) {
500+
offset = 0;
501+
} else {
502+
validateSafeInteger(offset, 'offset');
503+
}
504+
495505
length |= 0;
496506

497507
if (length === 0) {

lib/internal/fs/promises.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,12 @@ async function read(handle, buffer, offset, length, position) {
208208
validateFileHandle(handle);
209209
validateBuffer(buffer);
210210

211-
offset |= 0;
211+
if (offset == null) {
212+
offset = 0;
213+
} else {
214+
validateSafeInteger(offset, 'offset');
215+
}
216+
212217
length |= 0;
213218

214219
if (length === 0)

src/node_file.cc

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include "node_buffer.h"
2525
#include "node_process.h"
2626
#include "node_stat_watcher.h"
27-
#include "util.h"
27+
#include "util-inl.h"
2828

2929
#include "tracing/trace_event.h"
3030

@@ -1831,7 +1831,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
18311831
*
18321832
* 0 fd int32. file descriptor
18331833
* 1 buffer instance of Buffer
1834-
* 2 offset int32. offset to start reading into inside buffer
1834+
* 2 offset int64. offset to start reading into inside buffer
18351835
* 3 length int32. length to read
18361836
* 4 position int64. file position - -1 for current position
18371837
*/
@@ -1849,15 +1849,17 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
18491849
char* buffer_data = Buffer::Data(buffer_obj);
18501850
size_t buffer_length = Buffer::Length(buffer_obj);
18511851

1852-
CHECK(args[2]->IsInt32());
1853-
const size_t off = static_cast<size_t>(args[2].As<Int32>()->Value());
1854-
CHECK_LT(off, buffer_length);
1852+
CHECK(IsSafeJsInt(args[2]));
1853+
const int64_t off_64 = args[2].As<Integer>()->Value();
1854+
CHECK_GE(off_64, 0);
1855+
CHECK_LT(static_cast<uint64_t>(off_64), buffer_length);
1856+
const size_t off = static_cast<size_t>(off_64);
18551857

18561858
CHECK(args[3]->IsInt32());
18571859
const size_t len = static_cast<size_t>(args[3].As<Int32>()->Value());
18581860
CHECK(Buffer::IsWithinBounds(off, len, buffer_length));
18591861

1860-
CHECK(args[4]->IsNumber());
1862+
CHECK(IsSafeJsInt(args[4]));
18611863
const int64_t pos = args[4].As<Integer>()->Value();
18621864

18631865
char* buf = buffer_data + off;

src/util-inl.h

Lines changed: 12 additions & 0 deletions
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 <cmath>
2728
#include <cstring>
2829
#include "util.h"
2930

@@ -491,6 +492,17 @@ void ArrayBufferViewContents<T, S>::Read(v8::Local<v8::ArrayBufferView> abv) {
491492
}
492493
}
493494

495+
// ECMA262 20.1.2.5
496+
inline bool IsSafeJsInt(v8::Local<v8::Value> v) {
497+
if (!v->IsNumber()) return false;
498+
double v_d = v.As<v8::Number>()->Value();
499+
if (std::isnan(v_d)) return false;
500+
if (std::isinf(v_d)) return false;
501+
if (std::trunc(v_d) != v_d) return false; // not int
502+
if (std::abs(v_d) <= static_cast<double>(kMaxSafeJsInteger)) return true;
503+
return false;
504+
}
505+
494506
} // namespace node
495507

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

src/util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ void DumpBacktrace(FILE* fp);
168168

169169
#define UNREACHABLE() ABORT()
170170

171+
constexpr int64_t kMaxSafeJsInteger = 9007199254740991; // 2^53-1
172+
173+
inline bool IsSafeJsInt(v8::Local<v8::Value> v);
174+
171175
// TAILQ-style intrusive list node.
172176
template <typename T>
173177
class ListNode;

0 commit comments

Comments
 (0)