diff --git a/src/node_messaging.cc b/src/node_messaging.cc index aa4b92bf749eac..6e8e3e8ad76e03 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -289,6 +289,15 @@ Maybe Message::Serialize(Environment* env, // take ownership of its memory, copying the buffer will have to do. if (!ab->IsNeuterable() || ab->IsExternal()) continue; + if (std::find(array_buffers.begin(), array_buffers.end(), ab) != + array_buffers.end()) { + ThrowDataCloneException( + env, + FIXED_ONE_BYTE_STRING( + env->isolate(), + "Transfer list contains duplicate ArrayBuffer")); + return Nothing(); + } // We simply use the array index in the `array_buffers` list as the // ID that we write into the serialized buffer. uint32_t id = array_buffers.size(); @@ -314,6 +323,15 @@ Maybe Message::Serialize(Environment* env, "MessagePort in transfer list is already detached")); return Nothing(); } + if (std::find(delegate.ports_.begin(), delegate.ports_.end(), port) != + delegate.ports_.end()) { + ThrowDataCloneException( + env, + FIXED_ONE_BYTE_STRING( + env->isolate(), + "Transfer list contains duplicate MessagePort")); + return Nothing(); + } delegate.ports_.push_back(port); continue; } @@ -601,6 +619,7 @@ void MessagePort::OnClose() { } std::unique_ptr MessagePort::Detach() { + CHECK(data_); Mutex::ScopedLock lock(data_->mutex_); data_->owner_ = nullptr; return std::move(data_); diff --git a/test/parallel/test-worker-message-port-transfer-duplicate.js b/test/parallel/test-worker-message-port-transfer-duplicate.js new file mode 100644 index 00000000000000..c893556d8d2c48 --- /dev/null +++ b/test/parallel/test-worker-message-port-transfer-duplicate.js @@ -0,0 +1,29 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); + +// Test that passing duplicate transferrables in the transfer list throws +// DataCloneError exceptions. + +{ + const { port1, port2 } = new MessageChannel(); + port2.once('message', common.mustNotCall()); + + const port3 = new MessageChannel().port1; + assert.throws(() => { + port1.postMessage(port3, [port3, port3]); + }, /^DataCloneError: Transfer list contains duplicate MessagePort$/); + port1.close(); +} + +{ + const { port1, port2 } = new MessageChannel(); + port2.once('message', common.mustNotCall()); + + const buf = new Uint8Array(10); + assert.throws(() => { + port1.postMessage(buf, [buf.buffer, buf.buffer]); + }, /^DataCloneError: Transfer list contains duplicate ArrayBuffer$/); + port1.close(); +}