Commit 8b61d57e authored by binji's avatar binji Committed by Commit bot

Disallow SharedArrayBuffer in postMessage transfer list

This is the Blink-side change to match the V8 change here:
https://codereview.chromium.org/2570433005.

BUG=676063

Review-Url: https://codereview.chromium.org/2615803002
Cr-Commit-Position: refs/heads/master@{#442111}
parent eb9098d5
......@@ -2346,9 +2346,6 @@ crbug.com/657748 imported/csswg-test/css-display-3/display-contents-text-only-00
crbug.com/676229 [ Linux ] plugins/mouse-click-plugin-clears-selection.html [ Failure Pass ]
# Temporarily disable sharedarraybuffer test to land API change in v8
crbug.com/676063 virtual/sharedarraybuffer/fast/workers/worker-sharedarraybuffer-transfer.html [ Pass Timeout Crash ]
# Flaky on Win7 (dbg)
crbug.com/677145 [ Win7 Debug ] inspector/tracing/timeline-js/timeline-script-tag-1.html [ Pass Failure ]
crbug.com/677145 [ Win7 Debug ] virtual/threaded/inspector/tracing/timeline-js/timeline-script-tag-1.html [ Pass Failure ]
......
function verifyArray(ta, length) {
var i;
for (i = 0; i < length; ++i) {
if (ta[i] != i) {
postMessage("FAIL: Transferred data is incorrect. Expected " +
......@@ -18,8 +19,9 @@ function verifyArrayType(ta, name) {
}
self.addEventListener('message', function(e) {
var i;
var ab;
var sab;
var sab2;
var ta;
switch (e.data.name) {
......@@ -43,6 +45,22 @@ self.addEventListener('message', function(e) {
verifyArray(ta, e.data.length);
break;
case 'ArrayBufferAndSharedArrayBuffer':
ab = e.data.ab;
sab = e.data.sab;
verifyArray(new Uint8Array(ab), e.data.abByteLength);
verifyArray(new Uint8Array(sab), e.data.sabByteLength);
break;
case 'SharedArrayBufferTwice':
sab = e.data.sab;
sab2 = e.data.sab2;
if (sab !== sab2) {
postMessage('FAIL: Expected two SharedArrayBuffers to be equal.');
}
verifyArray(new Uint8Array(sab), e.data.sabByteLength);
break;
default:
postMessage("ERROR: unknown command " + e.data.name);
break;
......
......@@ -9,53 +9,129 @@ function log(message)
document.getElementById("result").innerHTML += message + "</br>";
}
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
var testCases = [
{name: "SharedArrayBuffer"},
{name: "Int8Array"},
{name: "Uint8Array"},
{name: "Uint8ClampedArray"},
{name: "Int16Array"},
{name: "Uint16Array"},
{name: "Int32Array"},
{name: "Uint32Array"},
{name: "Float32Array"},
{name: "Float64Array"}
"testSendSharedArrayBuffer",
"testSendInt8Array",
"testSendUint8Array",
"testSendUint8ClampedArray",
"testSendInt16Array",
"testSendUint16Array",
"testSendInt32Array",
"testSendUint32Array",
"testSendFloat32Array",
"testSendFloat64Array",
"testSendSharedArrayBufferTwice",
"testTransferArrayBufferAndSharedArrayBuffer"
];
var currentTestCase = 0;
var testIndex = 0;
function runTestCase(testCase) {
function runNextTest()
{
if (testIndex < testCases.length) {
testIndex++;
try {
window[testCases[testIndex - 1]]();
} catch (ex) {
log("FAIL: unexpected exception " + ex);
runNextTest();
}
} else {
log("DONE");
if (window.testRunner)
testRunner.notifyDone();
}
}
function testSendSharedArrayBuffer()
{
runSendTest("SharedArrayBuffer");
}
function testSendInt8Array()
{
runSendTest("Int8Array");
}
function testSendUint8Array()
{
runSendTest("Uint8Array");
}
function testSendUint8ClampedArray()
{
runSendTest("Uint8ClampedArray");
}
function testSendInt16Array()
{
runSendTest("Int16Array");
}
function testSendUint16Array()
{
runSendTest("Uint16Array");
}
function testSendInt32Array()
{
runSendTest("Int32Array");
}
function testSendUint32Array()
{
runSendTest("Uint32Array");
}
function testSendFloat32Array()
{
runSendTest("Float32Array");
}
function testSendFloat64Array()
{
runSendTest("Float64Array");
}
function initializeTypedArray(ta, length) {
var i;
for (i = 0; i < length; ++i)
ta[i] = i;
}
function runSendTest(name)
{
var length = 8;
var name = testCase.name;
var type = window[name];
var sab;
var ta;
var msg;
log("Running " + name + " test case");
if (testCase.name == 'SharedArrayBuffer') {
if (name == 'SharedArrayBuffer') {
sab = new SharedArrayBuffer(length);
ta = new Uint8Array(sab);
msg = {name: name, data: sab, length: length};
} else {
sab = new SharedArrayBuffer(length * type.BYTES_PER_ELEMENT);
ta = new type(sab);
msg = {name: name, data: ta, length: length};
}
var i;
for (i = 0; i < length; ++i)
ta[i] = i;
initializeTypedArray(ta, length);
// Don't allow passing a SharedArrayBuffer in the transfer list.
try {
worker.postMessage(msg, [sab]);
log("FAIL: Passing SharedArrayBuffer in the transfer list did not throw.");
} catch (e) {
log("PASS: Passing SharedArrayBuffer in the transfer list threw.");
}
// Without Atomics, we can't safely test modifying the contents of the
// SharedArrayBuffer. All we can test for now is that the SharedArrayBuffer
// is not neutered when transferred to a Worker.
if (testCase.name == 'SharedArrayBuffer')
worker.postMessage({name: name, data: sab, length: length}, [sab]);
else
worker.postMessage({name: name, data: ta, length: length}, [sab]);
worker.postMessage(msg);
if (sab.length === 0)
log("FAIL: SharedArrayBuffer was neutered during transfer.");
......@@ -74,25 +150,64 @@ function runTestCase(testCase) {
log("PASS: Original data not changed during transfer.");
}
function testTransferArrayBufferAndSharedArrayBuffer() {
var ab = new ArrayBuffer(4);
var sab = new SharedArrayBuffer(16);
var msg = {
name : 'ArrayBufferAndSharedArrayBuffer',
ab: ab,
abLength: ab.byteLength,
sab: sab,
sabLength: sab.byteLength,
};
log("Running TransferArrayBufferAndSharedArrayBuffer test case");
initializeTypedArray(new Uint8Array(ab), ab.byteLength);
initializeTypedArray(new Uint8Array(sab), sab.byteLength);
worker.postMessage(msg, [ab]);
if (ab.byteLength === 0)
log("PASS: ArrayBuffer was neutered during transfer.");
else
log("FAIL: ArrayBuffer was not neutered during transfer.");
}
function testSendSharedArrayBufferTwice() {
var sab = new SharedArrayBuffer(16);
var msg = {
name : 'SharedArrayBufferTwice',
sab: sab,
sabLength: sab.byteLength,
sab2: sab,
sab2Length: sab.byteLength,
};
log("Running SendSharedArrayBufferTwice test case");
initializeTypedArray(new Uint8Array(sab), sab.byteLength);
worker.postMessage(msg);
}
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
if (window.internals && internals.runtimeFlags.sharedArrayBufferEnabled && window.SharedArrayBuffer) {
var worker = new Worker('resources/worker-sharedarraybuffer-transfer.js');
runTestCase(testCases[currentTestCase]);
runNextTest();
worker.onmessage = function(e) {
if (e.data == 'DONE') {
// The current test case is finished.
if (++currentTestCase == testCases.length) {
log("DONE");
testRunner.notifyDone();
} else {
runTestCase(testCases[currentTestCase]);
}
} else {
if (e.data != 'DONE') {
// The worker sent a pass/fail message.
log(e.data);
} else {
runNextTest();
}
};
} else {
log("SharedArrayBuffers are not enabled -- skipping test.");
......
Test sharing SharedArrayBuffers between dedicated workers.
Running SharedArrayBuffer test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred data is OK.
Running Int8Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Uint8Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Uint8ClampedArray test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Int16Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Uint16Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Int32Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Uint32Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Float32Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running Float64Array test case
PASS: Passing SharedArrayBuffer in the transfer list threw.
PASS: SharedArrayBuffer not neutered during transfer.
PASS: Original data not changed during transfer.
PASS: Transferred array type is OK.
PASS: Transferred data is OK.
Running SendSharedArrayBufferTwice test case
PASS: Transferred data is OK.
Running TransferArrayBufferAndSharedArrayBuffer test case
PASS: ArrayBuffer was neutered during transfer.
PASS: Transferred data is OK.
PASS: Transferred data is OK.
DONE
......@@ -13,6 +13,7 @@
#include "bindings/core/v8/V8ImageData.h"
#include "bindings/core/v8/V8MessagePort.h"
#include "bindings/core/v8/V8OffscreenCanvas.h"
#include "bindings/core/v8/V8SharedArrayBuffer.h"
#include "core/dom/DOMArrayBufferBase.h"
#include "core/html/ImageData.h"
#include "platform/RuntimeEnabledFeatures.h"
......@@ -44,7 +45,9 @@ RefPtr<SerializedScriptValue> V8ScriptValueSerializer::serialize(
AutoReset<const ExceptionState*> reset(&m_exceptionState, &exceptionState);
// Prepare to transfer the provided transferables.
prepareTransfer(transferables);
prepareTransfer(transferables, exceptionState);
if (exceptionState.hadException())
return nullptr;
// Serialize the value and handle errors.
v8::TryCatch tryCatch(m_scriptState->isolate());
......@@ -70,7 +73,8 @@ RefPtr<SerializedScriptValue> V8ScriptValueSerializer::serialize(
return std::move(m_serializedScriptValue);
}
void V8ScriptValueSerializer::prepareTransfer(Transferables* transferables) {
void V8ScriptValueSerializer::prepareTransfer(Transferables* transferables,
ExceptionState& exceptionState) {
if (!transferables)
return;
m_transferables = transferables;
......@@ -78,30 +82,33 @@ void V8ScriptValueSerializer::prepareTransfer(Transferables* transferables) {
// Transfer array buffers.
for (uint32_t i = 0; i < transferables->arrayBuffers.size(); i++) {
DOMArrayBufferBase* arrayBuffer = transferables->arrayBuffers[i].get();
if (!arrayBuffer->isShared()) {
v8::Local<v8::Value> wrapper = ToV8(arrayBuffer, m_scriptState.get());
if (wrapper->IsArrayBuffer()) {
m_serializer.TransferArrayBuffer(
i, v8::Local<v8::ArrayBuffer>::Cast(wrapper));
} else if (wrapper->IsSharedArrayBuffer()) {
m_serializer.TransferSharedArrayBuffer(
i, v8::Local<v8::SharedArrayBuffer>::Cast(wrapper));
} else {
NOTREACHED() << "Unknown type of array buffer in transfer list.";
exceptionState.throwDOMException(
DataCloneError, "SharedArrayBuffer can not be in transfer list.");
return;
}
}
}
void V8ScriptValueSerializer::finalizeTransfer(ExceptionState& exceptionState) {
if (!m_transferables)
if (!m_transferables && m_sharedArrayBuffers.isEmpty())
return;
// TODO(jbroman): Strictly speaking, this is not correct; transfer should
// occur in the order of the transfer list.
// https://html.spec.whatwg.org/multipage/infrastructure.html#structuredclonewithtransfer
ArrayBufferArray arrayBuffers;
arrayBuffers.appendVector(m_transferables->arrayBuffers);
arrayBuffers.appendVector(m_sharedArrayBuffers);
v8::Isolate* isolate = m_scriptState->isolate();
m_serializedScriptValue->transferArrayBuffers(
isolate, m_transferables->arrayBuffers, exceptionState);
m_serializedScriptValue->transferArrayBuffers(isolate, arrayBuffers,
exceptionState);
if (exceptionState.hadException())
return;
......@@ -366,6 +373,34 @@ v8::Maybe<bool> V8ScriptValueSerializer::WriteHostObject(
return v8::Nothing<bool>();
}
v8::Maybe<uint32_t> V8ScriptValueSerializer::GetSharedArrayBufferId(
v8::Isolate* isolate,
v8::Local<v8::SharedArrayBuffer> v8SharedArrayBuffer) {
DOMSharedArrayBuffer* sharedArrayBuffer =
V8SharedArrayBuffer::toImpl(v8SharedArrayBuffer);
// The index returned from this function will be serialized into the data
// stream. When deserializing, this will be used to index into the
// arrayBufferContents array of the SerializedScriptValue.
//
// The v8::ValueSerializer will use the same index space for transferred
// ArrayBuffers, but those will all occur first, because their indexes are
// generated in order via v8::ValueSerializer::TransferArrayBuffer (see
// prepareTransfer above).
//
// So we offset all SharedArrayBuffer indexes by the number of transferred
// ArrayBuffers.
size_t index = m_sharedArrayBuffers.find(sharedArrayBuffer);
if (index == kNotFound) {
m_sharedArrayBuffers.append(sharedArrayBuffer);
index = m_sharedArrayBuffers.size() - 1;
}
if (m_transferables) {
index += m_transferables->arrayBuffers.size();
}
return v8::Just<uint32_t>(index);
}
void* V8ScriptValueSerializer::ReallocateBufferMemory(void* oldBuffer,
size_t size,
size_t* actualSize) {
......
......@@ -70,7 +70,7 @@ class GC_PLUGIN_IGNORE("https://crbug.com/644725")
// neuter objects in the source context).
// This separation is required by the spec (it prevents neutering from
// happening if there's a failure earlier in serialization).
void prepareTransfer(Transferables*);
void prepareTransfer(Transferables*, ExceptionState&);
void finalizeTransfer(ExceptionState&);
// Shared between File and FileList logic; does not write a leading tag.
......@@ -80,6 +80,9 @@ class GC_PLUGIN_IGNORE("https://crbug.com/644725")
void ThrowDataCloneError(v8::Local<v8::String> message) override;
v8::Maybe<bool> WriteHostObject(v8::Isolate*,
v8::Local<v8::Object> message) override;
v8::Maybe<uint32_t> GetSharedArrayBufferId(
v8::Isolate*,
v8::Local<v8::SharedArrayBuffer>) override;
void* ReallocateBufferMemory(void* oldBuffer,
size_t,
......@@ -92,6 +95,7 @@ class GC_PLUGIN_IGNORE("https://crbug.com/644725")
const Transferables* m_transferables = nullptr;
const ExceptionState* m_exceptionState = nullptr;
WebBlobInfoArray* m_blobInfoArray = nullptr;
ArrayBufferArray m_sharedArrayBuffers;
#if DCHECK_IS_ON()
bool m_serializeInvoked = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment