summaryrefslogtreecommitdiff
path: root/src/node_messaging.cc
diff options
context:
space:
mode:
authorTimothy Gu <timothygu99@gmail.com>2018-06-24 23:10:37 -0400
committerTimothy Gu <timothygu99@gmail.com>2018-07-03 22:54:03 -0400
commitf374d6aaf9a2a171c9cd100a4ca2d26a68f72cb8 (patch)
treede1cf48cf4a77bf4da01fbe04edf8d98356868c4 /src/node_messaging.cc
parent5f3bdb016a99d102ebafe86424e7761228ef7f70 (diff)
downloadandroid-node-v8-f374d6aaf9a2a171c9cd100a4ca2d26a68f72cb8.tar.gz
android-node-v8-f374d6aaf9a2a171c9cd100a4ca2d26a68f72cb8.tar.bz2
android-node-v8-f374d6aaf9a2a171c9cd100a4ca2d26a68f72cb8.zip
messaging: fix edge cases with transferring ports
Currently, transferring the port on which postMessage is called causes a segmentation fault, and transferring the target port causes a subsequent port.onmessage setting to throw, or a deadlock if onmessage is set before the postMessage. Fix both of these behaviors and align the methods more closely with the normative definitions in the HTML Standard. Also, per spec postMessage must not throw just because the ports are disentangled. Implement that behavior. PR-URL: https://github.com/nodejs/node/pull/21540 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'src/node_messaging.cc')
-rw-r--r--src/node_messaging.cc99
1 files changed, 76 insertions, 23 deletions
diff --git a/src/node_messaging.cc b/src/node_messaging.cc
index 7ddd3c4165..712add06d3 100644
--- a/src/node_messaging.cc
+++ b/src/node_messaging.cc
@@ -144,7 +144,7 @@ void Message::AddMessagePort(std::unique_ptr<MessagePortData>&& data) {
namespace {
-void ThrowDataCloneError(Environment* env, Local<String> message) {
+void ThrowDataCloneException(Environment* env, Local<String> message) {
Local<Value> argv[] = {
message,
FIXED_ONE_BYTE_STRING(env->isolate(), "DataCloneError")
@@ -168,7 +168,7 @@ class SerializerDelegate : public ValueSerializer::Delegate {
: env_(env), context_(context), msg_(m) {}
void ThrowDataCloneError(Local<String> message) override {
- ThrowDataCloneError(env_, message);
+ ThrowDataCloneException(env_, message);
}
Maybe<bool> WriteHostObject(Isolate* isolate, Local<Object> object) override {
@@ -239,7 +239,8 @@ class SerializerDelegate : public ValueSerializer::Delegate {
Maybe<bool> Message::Serialize(Environment* env,
Local<Context> context,
Local<Value> input,
- Local<Value> transfer_list_v) {
+ Local<Value> transfer_list_v,
+ Local<Object> source_port) {
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(context);
@@ -273,8 +274,23 @@ Maybe<bool> Message::Serialize(Environment* env,
continue;
} else if (env->message_port_constructor_template()
->HasInstance(entry)) {
+ // Check if the source MessagePort is being transferred.
+ if (!source_port.IsEmpty() && entry == source_port) {
+ ThrowDataCloneException(
+ env,
+ FIXED_ONE_BYTE_STRING(env->isolate(),
+ "Transfer list contains source port"));
+ return Nothing<bool>();
+ }
MessagePort* port = Unwrap<MessagePort>(entry.As<Object>());
- CHECK_NE(port, nullptr);
+ if (port == nullptr || port->IsDetached()) {
+ ThrowDataCloneException(
+ env,
+ FIXED_ONE_BYTE_STRING(
+ env->isolate(),
+ "MessagePort in transfer list is already detached"));
+ return Nothing<bool>();
+ }
delegate.ports_.push_back(port);
continue;
}
@@ -410,6 +426,10 @@ uv_async_t* MessagePort::async() {
return reinterpret_cast<uv_async_t*>(GetHandle());
}
+bool MessagePort::IsDetached() const {
+ return data_ == nullptr || IsHandleClosing();
+}
+
void MessagePort::TriggerAsync() {
if (IsHandleClosing()) return;
CHECK_EQ(uv_async_send(async()), 0);
@@ -552,36 +572,69 @@ std::unique_ptr<MessagePortData> MessagePort::Detach() {
}
-void MessagePort::Send(Message&& message) {
- Mutex::ScopedLock lock(*data_->sibling_mutex_);
- if (data_->sibling_ == nullptr)
- return;
- data_->sibling_->AddToIncomingQueue(std::move(message));
-}
+Maybe<bool> MessagePort::PostMessage(Environment* env,
+ Local<Value> message_v,
+ Local<Value> transfer_v) {
+ Isolate* isolate = env->isolate();
+ Local<Object> obj = object(isolate);
+ Local<Context> context = obj->CreationContext();
-void MessagePort::Send(const FunctionCallbackInfo<Value>& args) {
- Environment* env = Environment::GetCurrent(args);
- Local<Context> context = object(env->isolate())->CreationContext();
Message msg;
- if (msg.Serialize(env, context, args[0], args[1])
- .IsNothing()) {
- return;
+
+ // Per spec, we need to both check if transfer list has the source port, and
+ // serialize the input message, even if the MessagePort is closed or detached.
+
+ Maybe<bool> serialization_maybe =
+ msg.Serialize(env, context, message_v, transfer_v, obj);
+ if (data_ == nullptr) {
+ return serialization_maybe;
+ }
+ if (serialization_maybe.IsNothing()) {
+ return Nothing<bool>();
+ }
+
+ Mutex::ScopedLock lock(*data_->sibling_mutex_);
+ bool doomed = false;
+
+ // Check if the target port is posted to itself.
+ if (data_->sibling_ != nullptr) {
+ for (const auto& port_data : msg.message_ports()) {
+ if (data_->sibling_ == port_data.get()) {
+ doomed = true;
+ ProcessEmitWarning(env, "The target port was posted to itself, and "
+ "the communication channel was lost");
+ break;
+ }
+ }
}
- Send(std::move(msg));
+
+ if (data_->sibling_ == nullptr || doomed)
+ return Just(true);
+
+ data_->sibling_->AddToIncomingQueue(std::move(msg));
+ return Just(true);
}
void MessagePort::PostMessage(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- MessagePort* port;
- ASSIGN_OR_RETURN_UNWRAP(&port, args.This());
- if (!port->data_) {
- return THROW_ERR_CLOSED_MESSAGE_PORT(env);
- }
if (args.Length() == 0) {
return THROW_ERR_MISSING_ARGS(env, "Not enough arguments to "
"MessagePort.postMessage");
}
- port->Send(args);
+
+ MessagePort* port = Unwrap<MessagePort>(args.This());
+ // Even if the backing MessagePort object has already been deleted, we still
+ // want to serialize the message to ensure spec-compliant behavior w.r.t.
+ // transfers.
+ if (port == nullptr) {
+ Message msg;
+ Local<Object> obj = args.This();
+ Local<Context> context = obj->CreationContext();
+ USE(msg.Serialize(env, context, args[0], args[1], obj));
+ return;
+ }
+
+ port->PostMessage(env, args[0], args[1]);
}
void MessagePort::Start() {