Skip to content

Commit 1ba4732

Browse files
kylo5abyjasnell
authored andcommitted
buffer: make buflen in integer range
PR-URL: #51821 Fixes: #51817 Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent b71267e commit 1ba4732

File tree

2 files changed

+74
-12
lines changed

2 files changed

+74
-12
lines changed

Diff for: src/string_bytes.cc

+23-12
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ MaybeLocal<Value> ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate,
198198

199199
} // anonymous namespace
200200

201+
static size_t keep_buflen_in_range(size_t len) {
202+
if (len > static_cast<size_t>(std::numeric_limits<int>::max())) {
203+
return static_cast<size_t>(std::numeric_limits<int>::max());
204+
}
205+
return len;
206+
}
207+
201208
size_t StringBytes::WriteUCS2(
202209
Isolate* isolate, char* buf, size_t buflen, Local<String> str, int flags) {
203210
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
@@ -243,7 +250,7 @@ size_t StringBytes::Write(Isolate* isolate,
243250
enum encoding encoding) {
244251
HandleScope scope(isolate);
245252
size_t nbytes;
246-
253+
buflen = keep_buflen_in_range(buflen);
247254
CHECK(val->IsString() == true);
248255
Local<String> str = val.As<String>();
249256
String::ValueView input_view(isolate, str);
@@ -516,6 +523,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
516523
}
517524

518525
case ASCII:
526+
buflen = keep_buflen_in_range(buflen);
519527
if (simdutf::validate_ascii_with_errors(buf, buflen).error) {
520528
// The input contains non-ASCII bytes.
521529
char* out = node::UncheckedMalloc(buflen);
@@ -529,23 +537,23 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
529537
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
530538
}
531539

532-
case UTF8:
533-
{
534-
val = String::NewFromUtf8(isolate,
535-
buf,
536-
v8::NewStringType::kNormal,
537-
buflen);
538-
Local<String> str;
539-
if (!val.ToLocal(&str)) {
540-
*error = node::ERR_STRING_TOO_LONG(isolate);
541-
}
542-
return str;
540+
case UTF8: {
541+
buflen = keep_buflen_in_range(buflen);
542+
val =
543+
String::NewFromUtf8(isolate, buf, v8::NewStringType::kNormal, buflen);
544+
Local<String> str;
545+
if (!val.ToLocal(&str)) {
546+
*error = node::ERR_STRING_TOO_LONG(isolate);
543547
}
548+
return str;
549+
}
544550

545551
case LATIN1:
552+
buflen = keep_buflen_in_range(buflen);
546553
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
547554

548555
case BASE64: {
556+
buflen = keep_buflen_in_range(buflen);
549557
size_t dlen = simdutf::base64_length_from_binary(buflen);
550558
char* dst = node::UncheckedMalloc(dlen);
551559
if (dst == nullptr) {
@@ -560,6 +568,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
560568
}
561569

562570
case BASE64URL: {
571+
buflen = keep_buflen_in_range(buflen);
563572
size_t dlen =
564573
simdutf::base64_length_from_binary(buflen, simdutf::base64_url);
565574
char* dst = node::UncheckedMalloc(dlen);
@@ -576,6 +585,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
576585
}
577586

578587
case HEX: {
588+
buflen = keep_buflen_in_range(buflen);
579589
size_t dlen = buflen * 2;
580590
char* dst = node::UncheckedMalloc(dlen);
581591
if (dst == nullptr) {
@@ -589,6 +599,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
589599
}
590600

591601
case UCS2: {
602+
buflen = keep_buflen_in_range(buflen);
592603
size_t str_len = buflen / 2;
593604
if constexpr (IsBigEndian()) {
594605
uint16_t* dst = node::UncheckedMalloc<uint16_t>(str_len);

Diff for: test/pummel/test-buffer-large-size.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'use strict';
2+
const common = require('../common');
3+
4+
// Buffer with size > INT32_MAX
5+
common.skipIf32Bits();
6+
7+
// Test Buffer size larger than integer range
8+
const { test } = require('node:test');
9+
const assert = require('assert');
10+
const {
11+
SlowBuffer,
12+
} = require('buffer');
13+
const kStringMaxLength = require('buffer').constants.MAX_STRING_LENGTH;
14+
15+
const stringTooLongError = {
16+
message: `Cannot create a string longer than 0x${kStringMaxLength.toString(16)}` +
17+
' characters',
18+
code: 'ERR_STRING_TOO_LONG',
19+
name: 'Error',
20+
};
21+
22+
const size = 2 ** 31;
23+
24+
// Test Buffer.toString
25+
test('Buffer.toString with too long size', () => {
26+
try {
27+
assert.throws(() => SlowBuffer(size).toString('utf8'), stringTooLongError);
28+
assert.throws(() => Buffer.alloc(size).toString('utf8'), stringTooLongError);
29+
assert.throws(() => Buffer.allocUnsafe(size).toString('utf8'), stringTooLongError);
30+
assert.throws(() => Buffer.allocUnsafeSlow(size).toString('utf8'), stringTooLongError);
31+
} catch (e) {
32+
if (e.code !== 'ERR_MEMORY_ALLOCATION_FAILED') {
33+
throw e;
34+
}
35+
common.skip('insufficient space for Buffer.alloc');
36+
}
37+
});
38+
39+
// Test Buffer.write
40+
test('Buffer.write with too long size', () => {
41+
try {
42+
const buf = Buffer.alloc(size);
43+
assert.strictEqual(buf.write('a', 2, kStringMaxLength), 1);
44+
assert.strictEqual(buf.write('a', 2, size), 1);
45+
} catch (e) {
46+
if (e.code !== 'ERR_MEMORY_ALLOCATION_FAILED') {
47+
throw e;
48+
}
49+
common.skip('insufficient space for Buffer.alloc');
50+
}
51+
});

0 commit comments

Comments
 (0)