Bug 1818576 - Double check whether the streams are locked before transfer r=evilpie

Status checks are done in batch before any transfer, but transferring streams can change the status of parent/child of TransformStream and cause an assertion failure.

Differential Revision: https://phabricator.services.mozilla.com/D170897
This commit is contained in:
Kagami Sascha Rosylight 2023-05-16 14:32:44 +00:00
parent f8c9ab3ed0
commit 0638b18b45
2 changed files with 37 additions and 3 deletions

View File

@ -815,7 +815,13 @@ MOZ_CAN_RUN_SCRIPT static void SetUpCrossRealmTransformReadable(
bool ReadableStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId) {
// Step 1: If ! IsReadableStreamLocked(value) is true, throw a
// "DataCloneError" DOMException.
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler)
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
// check here as the state might have changed in case this ReadableStream is
// created by a TransferStream and being transferred together with the
// parent.)
if (IsReadableStreamLocked(this)) {
return false;
}
// Step 2: Let port1 be a new MessagePort in the current Realm.
// Step 3: Let port2 be a new MessagePort in the current Realm.
@ -902,7 +908,13 @@ bool ReadableStream::ReceiveTransfer(
bool WritableStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId) {
// Step 1: If ! IsWritableStreamLocked(value) is true, throw a
// "DataCloneError" DOMException.
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler)
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
// check here as the state might have changed in case this WritableStream is
// created by a TransferStream and being transferred together with the
// parent.)
if (IsWritableStreamLocked(this)) {
return false;
}
// Step 2: Let port1 be a new MessagePort in the current Realm.
// Step 3: Let port2 be a new MessagePort in the current Realm.
@ -994,7 +1006,13 @@ bool TransformStream::Transfer(JSContext* aCx, UniqueMessagePortId& aPortId1,
// "DataCloneError" DOMException.
// Step 4: If ! IsWritableStreamLocked(writable) is true, throw a
// "DataCloneError" DOMException.
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler)
// (Implemented in StructuredCloneHolder::CustomCanTransferHandler, but double
// check here as the state might have changed by
// Readable/WritableStream::Transfer in case the stream members of this
// TransformStream are being transferred together.)
if (IsReadableStreamLocked(mReadable) || IsWritableStreamLocked(mWritable)) {
return false;
}
// Step 5: Set dataHolder.[[readable]] to !
// StructuredSerializeWithTransfer(readable, « readable »).

View File

@ -0,0 +1,16 @@
const combinations = [
(t => [t, t.readable])(new TransformStream()),
(t => [t.readable, t])(new TransformStream()),
(t => [t, t.writable])(new TransformStream()),
(t => [t.writable, t])(new TransformStream()),
];
for (const combination of combinations) {
test(() => {
assert_throws_dom(
"DataCloneError",
() => structuredClone(combination, { transfer: combination }),
"structuredClone should throw"
);
}, `Transferring ${combination} should fail`);
}