buffer: throw exception when creating from non-Node.js Context

Throw an exception instead of crashing when attempting to create
`Buffer` objects from a Context that is not associated with
a Node.js `Environment`.

Possible alternatives for the future might be just returning
a plain `Uint8Array`, or working on providing `Buffer` for all
`Context`s.

PR-URL: https://github.com/nodejs/node/pull/23938
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
This commit is contained in:
Anna Henningsen 2018-10-28 15:59:20 +01:00 committed by Rich Trott
parent d8fb81fab3
commit 9c7d19104d
5 changed files with 70 additions and 11 deletions

View File

@ -613,6 +613,19 @@ An attempt was made to register something that is not a function as an
The type of an asynchronous resource was invalid. Note that users are also able The type of an asynchronous resource was invalid. Note that users are also able
to define their own types if using the public embedder API. to define their own types if using the public embedder API.
<a id="ERR_BUFFER_CONTEXT_NOT_AVAILABLE"></a>
### ERR_BUFFER_CONTEXT_NOT_AVAILABLE
An attempt was made to create a Node.js `Buffer` instance from addon or embedder
code, while in a JS engine Context that is not associated with a Node.js
instance. The data passed to the `Buffer` method will have been released
by the time the method returns.
When encountering this error, a possible alternative to creating a `Buffer`
instance is to create a normal `Uint8Array`, which only differs in the
prototype of the resulting object. `Uint8Array`s are generally accepted in all
Node.js core APIs where `Buffer`s are; they are available in all Contexts.
<a id="ERR_BUFFER_OUT_OF_BOUNDS"></a> <a id="ERR_BUFFER_OUT_OF_BOUNDS"></a>
### ERR_BUFFER_OUT_OF_BOUNDS ### ERR_BUFFER_OUT_OF_BOUNDS

View File

@ -271,7 +271,10 @@ MaybeLocal<Object> New(Isolate* isolate, size_t length) {
EscapableHandleScope handle_scope(isolate); EscapableHandleScope handle_scope(isolate);
Local<Object> obj; Local<Object> obj;
Environment* env = Environment::GetCurrent(isolate); Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. if (env == nullptr) {
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
return MaybeLocal<Object>();
}
if (Buffer::New(env, length).ToLocal(&obj)) if (Buffer::New(env, length).ToLocal(&obj))
return handle_scope.Escape(obj); return handle_scope.Escape(obj);
return Local<Object>(); return Local<Object>();
@ -314,7 +317,10 @@ MaybeLocal<Object> New(Environment* env, size_t length) {
MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) { MaybeLocal<Object> Copy(Isolate* isolate, const char* data, size_t length) {
EscapableHandleScope handle_scope(isolate); EscapableHandleScope handle_scope(isolate);
Environment* env = Environment::GetCurrent(isolate); Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. if (env == nullptr) {
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
return MaybeLocal<Object>();
}
Local<Object> obj; Local<Object> obj;
if (Buffer::Copy(env, data, length).ToLocal(&obj)) if (Buffer::Copy(env, data, length).ToLocal(&obj))
return handle_scope.Escape(obj); return handle_scope.Escape(obj);
@ -364,7 +370,11 @@ MaybeLocal<Object> New(Isolate* isolate,
void* hint) { void* hint) {
EscapableHandleScope handle_scope(isolate); EscapableHandleScope handle_scope(isolate);
Environment* env = Environment::GetCurrent(isolate); Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. if (env == nullptr) {
callback(data, hint);
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
return MaybeLocal<Object>();
}
Local<Object> obj; Local<Object> obj;
if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj)) if (Buffer::New(env, data, length, callback, hint).ToLocal(&obj))
return handle_scope.Escape(obj); return handle_scope.Escape(obj);
@ -403,7 +413,11 @@ MaybeLocal<Object> New(Environment* env,
MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) { MaybeLocal<Object> New(Isolate* isolate, char* data, size_t length) {
EscapableHandleScope handle_scope(isolate); EscapableHandleScope handle_scope(isolate);
Environment* env = Environment::GetCurrent(isolate); Environment* env = Environment::GetCurrent(isolate);
CHECK_NOT_NULL(env); // TODO(addaleax): Handle nullptr here. if (env == nullptr) {
free(data);
THROW_ERR_BUFFER_CONTEXT_NOT_AVAILABLE(isolate);
return MaybeLocal<Object>();
}
Local<Object> obj; Local<Object> obj;
if (Buffer::New(env, data, length).ToLocal(&obj)) if (Buffer::New(env, data, length).ToLocal(&obj))
return handle_scope.Escape(obj); return handle_scope.Escape(obj);

View File

@ -21,6 +21,7 @@ namespace node {
// a `Local<Value>` containing the TypeError with proper code and message // a `Local<Value>` containing the TypeError with proper code and message
#define ERRORS_WITH_CODE(V) \ #define ERRORS_WITH_CODE(V) \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \ V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
V(ERR_BUFFER_TOO_LARGE, Error) \ V(ERR_BUFFER_TOO_LARGE, Error) \
V(ERR_CANNOT_TRANSFER_OBJECT, TypeError) \ V(ERR_CANNOT_TRANSFER_OBJECT, TypeError) \
@ -64,6 +65,8 @@ namespace node {
// Errors with predefined static messages // Errors with predefined static messages
#define PREDEFINED_ERROR_MESSAGES(V) \ #define PREDEFINED_ERROR_MESSAGES(V) \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \
"Buffer is not available for the current Context") \
V(ERR_CANNOT_TRANSFER_OBJECT, "Cannot transfer object of unsupported type")\ V(ERR_CANNOT_TRANSFER_OBJECT, "Cannot transfer object of unsupported type")\
V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \ V(ERR_CLOSED_MESSAGE_PORT, "Cannot send data on closed MessagePort") \
V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \ V(ERR_CONSTRUCT_CALL_REQUIRED, "Cannot call constructor without `new`") \

View File

@ -1,4 +1,5 @@
#include <node.h> #include <node.h>
#include <node_buffer.h>
#include <assert.h> #include <assert.h>
namespace { namespace {
@ -15,6 +16,17 @@ using v8::Script;
using v8::String; using v8::String;
using v8::Value; using v8::Value;
inline void MakeBufferInNewContext(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
// This should throw an exception, rather than actually do anything.
MaybeLocal<Object> buf = node::Buffer::Copy(isolate, "foo", 3);
assert(buf.IsEmpty());
}
inline void RunInNewContext( inline void RunInNewContext(
const v8::FunctionCallbackInfo<v8::Value>& args) { const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
@ -41,13 +53,8 @@ inline void RunInNewContext(
inline void Initialize(Local<Object> exports, inline void Initialize(Local<Object> exports,
Local<Value> module, Local<Value> module,
Local<Context> context) { Local<Context> context) {
Isolate* isolate = context->GetIsolate(); NODE_SET_METHOD(exports, "runInNewContext", RunInNewContext);
Local<String> key = String::NewFromUtf8( NODE_SET_METHOD(exports, "makeBufferInNewContext", MakeBufferInNewContext);
isolate, "runInNewContext", NewStringType::kNormal).ToLocalChecked();
Local<Function> value = FunctionTemplate::New(isolate, RunInNewContext)
->GetFunction(context)
.ToLocalChecked();
assert(exports->Set(context, key, value).IsJust());
} }
} // anonymous namespace } // anonymous namespace

View File

@ -0,0 +1,22 @@
'use strict';
const common = require('../../common');
const assert = require('assert');
const {
makeBufferInNewContext
} = require(`./build/${common.buildType}/binding`);
// Because the `Buffer` function and its protoype property only (currently)
// exist in a Node.js instances main context, trying to create buffers from
// another context throws an exception.
try {
makeBufferInNewContext();
} catch (exception) {
assert.strictEqual(exception.constructor.name, 'Error');
assert(!(exception.constructor instanceof Error));
assert.strictEqual(exception.code, 'ERR_BUFFER_CONTEXT_NOT_AVAILABLE');
assert.strictEqual(exception.message,
'Buffer is not available for the current Context');
}