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

string_decoder: fix crash when calling __proto__.write() using C++ #42061

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/node_errors.h
Expand Up @@ -35,6 +35,7 @@ void OnFatalError(const char* location, const char* message);
V(ERR_CLOSED_MESSAGE_PORT, Error) \
V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \
V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \
V(ERR_ILLEGAL_CONSTRUCTOR, TypeError) \
V(ERR_CRYPTO_INITIALIZATION_FAILED, Error) \
V(ERR_CRYPTO_INVALID_AUTH_TAG, TypeError) \
V(ERR_CRYPTO_INVALID_COUNTER, TypeError) \
Expand Down Expand Up @@ -124,6 +125,7 @@ ERRORS_WITH_CODE(V)
"Buffer is not available for the current Context") \
V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \
V(ERR_CONSTRUCT_CALL_INVALID, "Constructor cannot be called") \
V(ERR_ILLEGAL_CONSTRUCTOR, "Illegal constructor") \
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \
V(ERR_CRYPTO_INITIALIZATION_FAILED, "Initialization failed") \
V(ERR_CRYPTO_INVALID_AUTH_TAG, "Invalid authentication tag") \
Expand Down
22 changes: 16 additions & 6 deletions src/string_decoder.cc
Expand Up @@ -37,7 +37,7 @@ MaybeLocal<String> MakeString(Isolate* isolate,
v8::NewStringType::kNormal,
length);
if (utf8_string.IsEmpty()) {
isolate->ThrowException(node::ERR_STRING_TOO_LONG(isolate));
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
return MaybeLocal<String>();
} else {
return utf8_string;
Expand Down Expand Up @@ -264,18 +264,28 @@ MaybeLocal<String> StringDecoder::FlushData(Isolate* isolate) {
namespace {

void DecodeData(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();

Local<Value> decoder_value = args[0];
if (!decoder_value->IsArrayBufferView()) {
isolate->ThrowException(ERR_ILLEGAL_CONSTRUCTOR(isolate));
return;
}

StringDecoder* decoder =
reinterpret_cast<StringDecoder*>(Buffer::Data(args[0]));
reinterpret_cast<StringDecoder*>(Buffer::Data(decoder_value));
CHECK_NOT_NULL(decoder);

CHECK(args[1]->IsArrayBufferView());
ArrayBufferViewContents<char> content(args[1].As<ArrayBufferView>());
size_t length = content.length();

MaybeLocal<String> ret =
decoder->DecodeData(args.GetIsolate(), content.data(), &length);
if (!ret.IsEmpty())
args.GetReturnValue().Set(ret.ToLocalChecked());
Local<String> decoded_data;
if (!decoder->DecodeData(isolate,
content.data(),
&length).ToLocal(&decoded_data)) return;

args.GetReturnValue().Set(decoded_data);
}

void FlushData(const FunctionCallbackInfo<Value>& args) {
Expand Down
7 changes: 7 additions & 0 deletions test/parallel/test-string-decoder.js
Expand Up @@ -210,6 +210,13 @@ if (common.enoughTestMem) {
);
}

assert.throws(
() => new StringDecoder('utf8').__proto__.write(Buffer.from('abc')), // eslint-disable-line no-proto
{
code: 'ERR_ILLEGAL_CONSTRUCTOR',
}
);

// Test verifies that StringDecoder will correctly decode the given input
// buffer with the given encoding to the expected output. It will attempt all
// possible ways to write() the input buffer, see writeSequences(). The
Expand Down