mirror of https://github.com/nodejs/node.git
src: migrate string_bytes.cc to throw errors with code
PR-URL: https://github.com/nodejs/node/pull/19739 Fixes: https://github.com/nodejs/node/issues/3175 Fixes: https://github.com/nodejs/node/issues/9489 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>pull/19739/merge
parent
289d152ce0
commit
63eb267c34
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
#include "node_internals.h"
|
#include "node_internals.h"
|
||||||
|
#include "node_errors.h"
|
||||||
#include "node_buffer.h"
|
#include "node_buffer.h"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -36,20 +37,6 @@
|
||||||
// use external string resources.
|
// use external string resources.
|
||||||
#define EXTERN_APEX 0xFBEE9
|
#define EXTERN_APEX 0xFBEE9
|
||||||
|
|
||||||
// TODO(addaleax): These should all have better error messages. In particular,
|
|
||||||
// they should mention what the actual limits are.
|
|
||||||
#define SB_MALLOC_FAILED_ERROR \
|
|
||||||
v8::Exception::Error(OneByteString(isolate, "\"toString()\" failed"))
|
|
||||||
|
|
||||||
#define SB_STRING_TOO_LONG_ERROR \
|
|
||||||
v8::Exception::Error(OneByteString(isolate, "\"toString()\" failed"))
|
|
||||||
|
|
||||||
#define SB_BUFFER_CREATION_ERROR \
|
|
||||||
v8::Exception::Error(OneByteString(isolate, "\"toString()\" failed"))
|
|
||||||
|
|
||||||
#define SB_BUFFER_SIZE_EXCEEDED_ERROR \
|
|
||||||
v8::Exception::Error(OneByteString(isolate, "\"toString()\" failed"))
|
|
||||||
|
|
||||||
namespace node {
|
namespace node {
|
||||||
|
|
||||||
using v8::HandleScope;
|
using v8::HandleScope;
|
||||||
|
@ -93,7 +80,7 @@ class ExternString: public ResourceType {
|
||||||
|
|
||||||
TypeName* new_data = node::UncheckedMalloc<TypeName>(length);
|
TypeName* new_data = node::UncheckedMalloc<TypeName>(length);
|
||||||
if (new_data == nullptr) {
|
if (new_data == nullptr) {
|
||||||
*error = SB_MALLOC_FAILED_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
memcpy(new_data, data, length * sizeof(*new_data));
|
memcpy(new_data, data, length * sizeof(*new_data));
|
||||||
|
@ -126,7 +113,7 @@ class ExternString: public ResourceType {
|
||||||
|
|
||||||
if (str.IsEmpty()) {
|
if (str.IsEmpty()) {
|
||||||
delete h_str;
|
delete h_str;
|
||||||
*error = SB_STRING_TOO_LONG_ERROR;
|
*error = node::ERR_STRING_TOO_LARGE(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +170,7 @@ MaybeLocal<Value> ExternOneByteString::NewSimpleFromCopy(Isolate* isolate,
|
||||||
v8::NewStringType::kNormal,
|
v8::NewStringType::kNormal,
|
||||||
length);
|
length);
|
||||||
if (str.IsEmpty()) {
|
if (str.IsEmpty()) {
|
||||||
*error = SB_STRING_TOO_LONG_ERROR;
|
*error = node::ERR_STRING_TOO_LARGE(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
return str.ToLocalChecked();
|
return str.ToLocalChecked();
|
||||||
|
@ -201,7 +188,7 @@ MaybeLocal<Value> ExternTwoByteString::NewSimpleFromCopy(Isolate* isolate,
|
||||||
v8::NewStringType::kNormal,
|
v8::NewStringType::kNormal,
|
||||||
length);
|
length);
|
||||||
if (str.IsEmpty()) {
|
if (str.IsEmpty()) {
|
||||||
*error = SB_STRING_TOO_LONG_ERROR;
|
*error = node::ERR_STRING_TOO_LARGE(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
return str.ToLocalChecked();
|
return str.ToLocalChecked();
|
||||||
|
@ -616,7 +603,7 @@ static size_t hex_encode(const char* src, size_t slen, char* dst, size_t dlen) {
|
||||||
#define CHECK_BUFLEN_IN_RANGE(len) \
|
#define CHECK_BUFLEN_IN_RANGE(len) \
|
||||||
do { \
|
do { \
|
||||||
if ((len) > Buffer::kMaxLength) { \
|
if ((len) > Buffer::kMaxLength) { \
|
||||||
*error = SB_BUFFER_SIZE_EXCEEDED_ERROR; \
|
*error = node::ERR_BUFFER_TOO_LARGE(isolate); \
|
||||||
return MaybeLocal<Value>(); \
|
return MaybeLocal<Value>(); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
@ -639,9 +626,13 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
switch (encoding) {
|
switch (encoding) {
|
||||||
case BUFFER:
|
case BUFFER:
|
||||||
{
|
{
|
||||||
|
if (buflen > node::Buffer::kMaxLength) {
|
||||||
|
*error = node::ERR_BUFFER_TOO_LARGE(isolate);
|
||||||
|
return MaybeLocal<Value>();
|
||||||
|
}
|
||||||
auto maybe_buf = Buffer::Copy(isolate, buf, buflen);
|
auto maybe_buf = Buffer::Copy(isolate, buf, buflen);
|
||||||
if (maybe_buf.IsEmpty()) {
|
if (maybe_buf.IsEmpty()) {
|
||||||
*error = SB_BUFFER_CREATION_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
return maybe_buf.ToLocalChecked();
|
return maybe_buf.ToLocalChecked();
|
||||||
|
@ -651,7 +642,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
if (contains_non_ascii(buf, buflen)) {
|
if (contains_non_ascii(buf, buflen)) {
|
||||||
char* out = node::UncheckedMalloc(buflen);
|
char* out = node::UncheckedMalloc(buflen);
|
||||||
if (out == nullptr) {
|
if (out == nullptr) {
|
||||||
*error = SB_MALLOC_FAILED_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
force_ascii(buf, out, buflen);
|
force_ascii(buf, out, buflen);
|
||||||
|
@ -666,7 +657,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
v8::NewStringType::kNormal,
|
v8::NewStringType::kNormal,
|
||||||
buflen);
|
buflen);
|
||||||
if (val.IsEmpty()) {
|
if (val.IsEmpty()) {
|
||||||
*error = SB_STRING_TOO_LONG_ERROR;
|
*error = node::ERR_STRING_TOO_LARGE(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
return val.ToLocalChecked();
|
return val.ToLocalChecked();
|
||||||
|
@ -678,7 +669,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
size_t dlen = base64_encoded_size(buflen);
|
size_t dlen = base64_encoded_size(buflen);
|
||||||
char* dst = node::UncheckedMalloc(dlen);
|
char* dst = node::UncheckedMalloc(dlen);
|
||||||
if (dst == nullptr) {
|
if (dst == nullptr) {
|
||||||
*error = SB_MALLOC_FAILED_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,7 +683,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
size_t dlen = buflen * 2;
|
size_t dlen = buflen * 2;
|
||||||
char* dst = node::UncheckedMalloc(dlen);
|
char* dst = node::UncheckedMalloc(dlen);
|
||||||
if (dst == nullptr) {
|
if (dst == nullptr) {
|
||||||
*error = SB_MALLOC_FAILED_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
size_t written = hex_encode(buf, buflen, dst, dlen);
|
size_t written = hex_encode(buf, buflen, dst, dlen);
|
||||||
|
@ -723,7 +714,7 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
|
||||||
if (IsBigEndian()) {
|
if (IsBigEndian()) {
|
||||||
uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen);
|
uint16_t* dst = node::UncheckedMalloc<uint16_t>(buflen);
|
||||||
if (dst == nullptr) {
|
if (dst == nullptr) {
|
||||||
*error = SB_MALLOC_FAILED_ERROR;
|
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
|
||||||
return MaybeLocal<Value>();
|
return MaybeLocal<Value>();
|
||||||
}
|
}
|
||||||
size_t nbytes = buflen * sizeof(uint16_t);
|
size_t nbytes = buflen * sizeof(uint16_t);
|
||||||
|
|
|
@ -6,7 +6,6 @@ if (!common.enoughTestMem)
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
const binding = require(`./build/${common.buildType}/binding`);
|
const binding = require(`./build/${common.buildType}/binding`);
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
// v8 fails silently if string length > v8::String::kMaxLength
|
// v8 fails silently if string length > v8::String::kMaxLength
|
||||||
// v8::String::kMaxLength defined in v8.h
|
// v8::String::kMaxLength defined in v8.h
|
||||||
|
@ -25,6 +24,11 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('ascii');
|
buf.toString('ascii');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
|
@ -6,7 +6,6 @@ if (!common.enoughTestMem)
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
const binding = require(`./build/${common.buildType}/binding`);
|
const binding = require(`./build/${common.buildType}/binding`);
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
// v8 fails silently if string length > v8::String::kMaxLength
|
// v8 fails silently if string length > v8::String::kMaxLength
|
||||||
// v8::String::kMaxLength defined in v8.h
|
// v8::String::kMaxLength defined in v8.h
|
||||||
|
@ -25,6 +24,11 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('base64');
|
buf.toString('base64');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
|
@ -25,9 +25,14 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('latin1');
|
buf.toString('latin1');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
||||||
let maxString = buf.toString('latin1', 1);
|
let maxString = buf.toString('latin1', 1);
|
||||||
assert.strictEqual(maxString.length, kStringMaxLength);
|
assert.strictEqual(maxString.length, kStringMaxLength);
|
||||||
|
|
|
@ -6,7 +6,6 @@ if (!common.enoughTestMem)
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
const binding = require(`./build/${common.buildType}/binding`);
|
const binding = require(`./build/${common.buildType}/binding`);
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
// v8 fails silently if string length > v8::String::kMaxLength
|
// v8 fails silently if string length > v8::String::kMaxLength
|
||||||
// v8::String::kMaxLength defined in v8.h
|
// v8::String::kMaxLength defined in v8.h
|
||||||
|
@ -25,6 +24,11 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('hex');
|
buf.toString('hex');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
|
@ -25,10 +25,27 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
buf.toString();
|
|
||||||
}, /"toString\(\)" failed|Array buffer allocation failed/);
|
|
||||||
|
|
||||||
assert.throws(function() {
|
assert.throws(function() {
|
||||||
|
buf.toString();
|
||||||
|
}, function(e) {
|
||||||
|
if (e.message !== 'Array buffer allocation failed') {
|
||||||
|
common.expectsError({
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
})(e);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('utf8');
|
buf.toString('utf8');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
|
@ -6,7 +6,6 @@ if (!common.enoughTestMem)
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
const binding = require(`./build/${common.buildType}/binding`);
|
const binding = require(`./build/${common.buildType}/binding`);
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
// v8 fails silently if string length > v8::String::kMaxLength
|
// v8 fails silently if string length > v8::String::kMaxLength
|
||||||
// v8::String::kMaxLength defined in v8.h
|
// v8::String::kMaxLength defined in v8.h
|
||||||
|
@ -25,6 +24,12 @@ try {
|
||||||
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
if (!binding.ensureAllocation(2 * kStringMaxLength))
|
||||||
common.skip(skipMessage);
|
common.skip(skipMessage);
|
||||||
|
|
||||||
assert.throws(function() {
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
|
||||||
|
common.expectsError(function() {
|
||||||
buf.toString('utf16le');
|
buf.toString('utf16le');
|
||||||
}, /"toString\(\)" failed/);
|
}, {
|
||||||
|
message: `Cannot create a string larger than 0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
});
|
||||||
|
|
|
@ -32,8 +32,15 @@ stream.end();
|
||||||
stream.on('finish', common.mustCall(function() {
|
stream.on('finish', common.mustCall(function() {
|
||||||
fs.readFile(file, 'utf8', common.mustCall(function(err, buf) {
|
fs.readFile(file, 'utf8', common.mustCall(function(err, buf) {
|
||||||
assert.ok(err instanceof Error);
|
assert.ok(err instanceof Error);
|
||||||
assert.ok(/^(Array buffer allocation failed|"toString\(\)" failed)$/
|
if (err.message !== 'Array buffer allocation failed') {
|
||||||
.test(err.message));
|
const stringLengthHex = kStringMaxLength.toString(16);
|
||||||
|
common.expectsError({
|
||||||
|
message: 'Cannot create a string larger than ' +
|
||||||
|
`0x${stringLengthHex} bytes`,
|
||||||
|
code: 'ERR_STRING_TOO_LARGE',
|
||||||
|
type: Error
|
||||||
|
})(err);
|
||||||
|
}
|
||||||
assert.strictEqual(buf, undefined);
|
assert.strictEqual(buf, undefined);
|
||||||
}));
|
}));
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in New Issue