Skip to content

Commit

Permalink
Error stream if CopyDataBlockBytes assertions do not hold
Browse files Browse the repository at this point in the history
  • Loading branch information
MattiasBuelens committed Sep 8, 2024
1 parent 1f1a16a commit 4cfd7ee
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 7 deletions.
28 changes: 24 additions & 4 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -3441,10 +3441,12 @@ The following abstract operations support the implementation of the
queue entry/byte length=]).
1. Let |destStart| be |pullIntoDescriptor|'s [=pull-into descriptor/byte offset=] +
|pullIntoDescriptor|'s [=pull-into descriptor/bytes filled=].
1. Perform ! [$CopyDataBlockBytes$](|pullIntoDescriptor|'s [=pull-into
descriptor/buffer=].\[[ArrayBufferData]], |destStart|,
|headOfQueue|'s [=readable byte stream queue entry/buffer=].\[[ArrayBufferData]],
|headOfQueue|'s [=readable byte stream queue entry/byte offset=], |bytesToCopy|).
1. If ! [$SafeCopyDataBlockBytes$](|pullIntoDescriptor|'s [=pull-into descriptor/buffer=],
|destStart|, |headOfQueue|'s [=readable byte stream queue entry/buffer=],
|headOfQueue|'s [=readable byte stream queue entry/byte offset=], |bytesToCopy|) is false,
1. Let |e| be a {{TypeError}} exception.
1. Perform ! [$ReadableByteStreamControllerError$](|controller|, |e|).
1. Return false.
1. If |headOfQueue|'s [=readable byte stream queue entry/byte length=] is |bytesToCopy|,
1. [=list/Remove=] |queue|[0].
1. Otherwise,
Expand Down Expand Up @@ -6848,6 +6850,24 @@ The following abstract operations are a grab-bag of utilities.
1. Return ? [$StructuredDeserialize$](|serialized|, [=the current Realm=]).
</div>

<div algorithm>
<dfn abstract-op lt="SafeCopyDataBlockBytes">SafeCopyDataBlockBytes(|toBuffer|, |toIndex|,
|fromBuffer|, |fromIndex|, |count|)</dfn> performs the following steps:

1. Assert: [$Type$](|toBuffer|) is Object.
1. Assert: |toBuffer| has an \[[ArrayBufferData]] internal slot.
1. Assert: [$Type$](|fromBuffer|) is Object.
1. Assert: |fromBuffer| has an \[[ArrayBufferData]] internal slot.
1. If |toBuffer| is |fromBuffer|, return false.
1. If ! [$IsDetachedBuffer$](|toBuffer|) is true, return false.
1. If ! [$IsDetachedBuffer$](|fromBuffer|) is true, return false.
1. If |toIndex| + |count| > |toBuffer|.\[[ArrayBufferByteLength]], return false.
1. If |fromIndex| + |count| > |fromBuffer|.\[[ArrayBufferByteLength]], return false.
1. Perform ! [$CopyDataBlockBytes$](|toBuffer|.\[[ArrayBufferData]], |toIndex|,
|fromBuffer|.\[[ArrayBufferData]], |fromIndex|, |count|).
1. Return true.
</div>

<h2 id="other-specs">Using streams in other specifications</h2>

Much of this standard concerns itself with the internal machinery of streams. Other specifications
Expand Down
21 changes: 21 additions & 0 deletions reference-implementation/lib/abstract-ops/miscellaneous.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
const { CopyDataBlockBytes, IsDetachedBuffer } = require('./ecmascript');

exports.IsNonNegativeNumber = v => {
if (typeof v !== 'number') {
Expand All @@ -20,3 +21,23 @@ exports.CloneAsUint8Array = O => {
const buffer = O.buffer.slice(O.byteOffset, O.byteOffset + O.byteLength);
return new Uint8Array(buffer);
};

exports.SafeCopyDataBlockBytes = (toBuffer, toIndex, fromBuffer, fromIndex, count) => {
if (toBuffer === fromBuffer) {
return false;
}
if (IsDetachedBuffer(toBuffer) === true) {
return false;
}
if (IsDetachedBuffer(fromBuffer) === true) {
return false;
}
if (toIndex + count > toBuffer.byteLength) {
return false;
}
if (fromIndex + count > fromBuffer.byteLength) {
return false;
}
CopyDataBlockBytes(toBuffer, toIndex, fromBuffer, fromIndex, count);
return true;
};
13 changes: 10 additions & 3 deletions reference-implementation/lib/abstract-ops/readable-streams.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ const assert = require('assert');
const { promiseResolvedWith, promiseRejectedWith, newPromise, resolvePromise, rejectPromise, uponPromise,
setPromiseIsHandledToTrue, waitForAllPromise, transformPromiseWith, uponFulfillment, uponRejection } =
require('../helpers/webidl.js');
const { CanTransferArrayBuffer, Call, CopyDataBlockBytes, CreateArrayFromList, GetIterator, GetMethod, IsDetachedBuffer,
const { CanTransferArrayBuffer, Call, CreateArrayFromList, GetIterator, GetMethod, IsDetachedBuffer,
IteratorComplete, IteratorNext, IteratorValue, TransferArrayBuffer, typeIsObject } = require('./ecmascript.js');
const { CloneAsUint8Array, IsNonNegativeNumber } = require('./miscellaneous.js');
const { CloneAsUint8Array, IsNonNegativeNumber, SafeCopyDataBlockBytes } = require('./miscellaneous.js');
const { EnqueueValueWithSize, ResetQueue } = require('./queue-with-sizes.js');
const { AcquireWritableStreamDefaultWriter, IsWritableStreamLocked, WritableStreamAbort,
WritableStreamDefaultWriterCloseWithErrorPropagation, WritableStreamDefaultWriterRelease,
Expand Down Expand Up @@ -1447,7 +1447,14 @@ function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller,
const bytesToCopy = Math.min(totalBytesToCopyRemaining, headOfQueue.byteLength);

const destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled;
CopyDataBlockBytes(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy);

if (SafeCopyDataBlockBytes(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset,
bytesToCopy) === false) {
// This should never happen. Please report an issue if it does! https://github.com/whatwg/streams/issues
const e = new TypeError('Invalid buffer');
ReadableByteStreamControllerError(controller, e);
return false;
}

if (headOfQueue.byteLength === bytesToCopy) {
queue.shift();
Expand Down

0 comments on commit 4cfd7ee

Please sign in to comment.