Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

buffer: make buflen in integer range #51821

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions src/string_bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,13 @@ MaybeLocal<Value> ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate,

} // anonymous namespace

static size_t keep_buflen_in_range(size_t len) {
if (len > static_cast<size_t>(std::numeric_limits<int>::max())) {
return static_cast<size_t>(std::numeric_limits<int>::max());
}
return len;
}

size_t StringBytes::WriteUCS2(
Isolate* isolate, char* buf, size_t buflen, Local<String> str, int flags) {
uint16_t* const dst = reinterpret_cast<uint16_t*>(buf);
Expand Down Expand Up @@ -245,7 +252,7 @@ size_t StringBytes::Write(Isolate* isolate,
enum encoding encoding) {
HandleScope scope(isolate);
size_t nbytes;

buflen = keep_buflen_in_range(buflen);
CHECK(val->IsString() == true);
Local<String> str = val.As<String>();

Expand Down Expand Up @@ -545,6 +552,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
}

case ASCII:
buflen = keep_buflen_in_range(buflen);
if (simdutf::validate_ascii_with_errors(buf, buflen).error) {
// The input contains non-ASCII bytes.
char* out = node::UncheckedMalloc(buflen);
Expand All @@ -558,23 +566,23 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
}

case UTF8:
{
val = String::NewFromUtf8(isolate,
buf,
v8::NewStringType::kNormal,
buflen);
Local<String> str;
if (!val.ToLocal(&str)) {
*error = node::ERR_STRING_TOO_LONG(isolate);
}
return str;
case UTF8: {
buflen = keep_buflen_in_range(buflen);
val =
String::NewFromUtf8(isolate, buf, v8::NewStringType::kNormal, buflen);
Local<String> str;
if (!val.ToLocal(&str)) {
*error = node::ERR_STRING_TOO_LONG(isolate);
}
return str;
}

case LATIN1:
buflen = keep_buflen_in_range(buflen);
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);

case BASE64: {
buflen = keep_buflen_in_range(buflen);
size_t dlen = simdutf::base64_length_from_binary(buflen);
char* dst = node::UncheckedMalloc(dlen);
if (dst == nullptr) {
Expand All @@ -589,6 +597,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
}

case BASE64URL: {
buflen = keep_buflen_in_range(buflen);
size_t dlen =
simdutf::base64_length_from_binary(buflen, simdutf::base64_url);
char* dst = node::UncheckedMalloc(dlen);
Expand All @@ -605,6 +614,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
}

case HEX: {
buflen = keep_buflen_in_range(buflen);
size_t dlen = buflen * 2;
char* dst = node::UncheckedMalloc(dlen);
if (dst == nullptr) {
Expand All @@ -618,6 +628,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
}

case UCS2: {
buflen = keep_buflen_in_range(buflen);
size_t str_len = buflen / 2;
if constexpr (IsBigEndian()) {
uint16_t* dst = node::UncheckedMalloc<uint16_t>(str_len);
Expand Down
51 changes: 51 additions & 0 deletions test/pummel/test-buffer-large-size.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';
const common = require('../common');

// Buffer with size > INT32_MAX
common.skipIf32Bits();

// Test Buffer size larger than integer range
const { test } = require('node:test');
const assert = require('assert');
const {
SlowBuffer,
} = require('buffer');
const kStringMaxLength = require('buffer').constants.MAX_STRING_LENGTH;
jasnell marked this conversation as resolved.
Show resolved Hide resolved

const stringTooLongError = {
message: `Cannot create a string longer than 0x${kStringMaxLength.toString(16)}` +
' characters',
code: 'ERR_STRING_TOO_LONG',
name: 'Error',
};

const size = 2 ** 31;

// Test Buffer.toString
test('Buffer.toString with too long size', () => {
try {
assert.throws(() => SlowBuffer(size).toString('utf8'), stringTooLongError);
assert.throws(() => Buffer.alloc(size).toString('utf8'), stringTooLongError);
assert.throws(() => Buffer.allocUnsafe(size).toString('utf8'), stringTooLongError);
assert.throws(() => Buffer.allocUnsafeSlow(size).toString('utf8'), stringTooLongError);
} catch (e) {
if (e.code !== 'ERR_MEMORY_ALLOCATION_FAILED') {
throw e;
}
common.skip('insufficient space for Buffer.alloc');
}
});

// Test Buffer.write
test('Buffer.write with too long size', () => {
try {
const buf = Buffer.alloc(size);
assert.strictEqual(buf.write('a', 2, kStringMaxLength), 1);
assert.strictEqual(buf.write('a', 2, size), 1);
} catch (e) {
if (e.code !== 'ERR_MEMORY_ALLOCATION_FAILED') {
throw e;
}
common.skip('insufficient space for Buffer.alloc');
}
});
Loading