mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
LibWeb: Prevent crash when encoding into detached buffer
This handles the case where data is encoded into a detached buffer.
This commit is contained in:
parent
8ec420bc28
commit
d37d0e4b59
Notes:
github-actions[bot]
2025-04-19 11:09:12 +00:00
Author: https://github.com/skyz1 Commit: https://github.com/LadybirdBrowser/ladybird/commit/d37d0e4b591 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4399 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/trflynn89
4 changed files with 301 additions and 0 deletions
|
@ -53,6 +53,12 @@ GC::Ref<JS::Uint8Array> TextEncoder::encode(String const& input) const
|
|||
// https://encoding.spec.whatwg.org/#dom-textencoder-encodeinto
|
||||
TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::Root<WebIDL::BufferSource> const& destination) const
|
||||
{
|
||||
// AD-HOC: Return early if destination is detached. This is not explicitly handled in the spec,
|
||||
// however no bytes are copied as destinations size is always zero in this case.
|
||||
// See: https://github.com/whatwg/encoding/issues/324
|
||||
if (destination->viewed_array_buffer()->is_detached())
|
||||
return { 0, 0 };
|
||||
|
||||
auto data = destination->viewed_array_buffer()->buffer().bytes().slice(destination->byte_offset(), destination->byte_length());
|
||||
|
||||
// 1. Let read be 0.
|
||||
|
@ -91,6 +97,7 @@ TextEncoderEncodeIntoResult TextEncoder::encode_into(String const& source, GC::R
|
|||
|
||||
// 6.4.1.3. Write the bytes in result into destination, with startingOffset set to written.
|
||||
// 6.4.1.4. Increment written by the number of bytes in result.
|
||||
// WARNING: See the warning for SharedArrayBuffer objects at https://encoding.spec.whatwg.org/#sharedarraybuffer-warning.
|
||||
for (auto byte : result)
|
||||
data[written++] = byte;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 111 tests
|
||||
|
||||
85 Pass
|
||||
26 Fail
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with Hi and destination length 0, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with Hi and destination length 0, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with A and destination length 10, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with A and destination length 10, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06 and destination length 4, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with <20><><EFBFBD>U+df06A and destination length 3, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with U+d834AU+df06A¥Hi and destination length 10, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with AU+df06 and destination length 4, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with AU+df06 and destination length 4, offset 4, filler random
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 0, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 4, filler 0
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 0, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 4, filler 128
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 0, filler random
|
||||
Pass encodeInto() into ArrayBuffer with ¥¥ and destination length 4, offset 4, filler random
|
||||
Pass encodeInto() into SharedArrayBuffer with ¥¥ and destination length 4, offset 4, filler random
|
||||
Fail Invalid encodeInto() destination: DataView, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: DataView, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int8Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int8Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int16Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int16Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int32Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Int32Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint16Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint16Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint32Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint32Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint8ClampedArray, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Uint8ClampedArray, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: BigInt64Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: BigInt64Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: BigUint64Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: BigUint64Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float16Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float16Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float32Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float32Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float64Array, backed by: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: Float64Array, backed by: SharedArrayBuffer
|
||||
Fail Invalid encodeInto() destination: ArrayBuffer
|
||||
Fail Invalid encodeInto() destination: SharedArrayBuffer
|
||||
Pass encodeInto() and a detached output buffer
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../resources/testharness.js"></script>
|
||||
<script src="../resources/testharnessreport.js"></script>
|
||||
<script src="../common/sab.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../encoding/encodeInto.any.js"></script>
|
162
Tests/LibWeb/Text/input/wpt-import/encoding/encodeInto.any.js
Normal file
162
Tests/LibWeb/Text/input/wpt-import/encoding/encodeInto.any.js
Normal file
|
@ -0,0 +1,162 @@
|
|||
// META: global=window,worker
|
||||
// META: script=/common/sab.js
|
||||
|
||||
[
|
||||
{
|
||||
"input": "Hi",
|
||||
"read": 0,
|
||||
"destinationLength": 0,
|
||||
"written": []
|
||||
},
|
||||
{
|
||||
"input": "A",
|
||||
"read": 1,
|
||||
"destinationLength": 10,
|
||||
"written": [0x41]
|
||||
},
|
||||
{
|
||||
"input": "\u{1D306}", // "\uD834\uDF06"
|
||||
"read": 2,
|
||||
"destinationLength": 4,
|
||||
"written": [0xF0, 0x9D, 0x8C, 0x86]
|
||||
},
|
||||
{
|
||||
"input": "\u{1D306}A",
|
||||
"read": 0,
|
||||
"destinationLength": 3,
|
||||
"written": []
|
||||
},
|
||||
{
|
||||
"input": "\uD834A\uDF06A¥Hi",
|
||||
"read": 5,
|
||||
"destinationLength": 10,
|
||||
"written": [0xEF, 0xBF, 0xBD, 0x41, 0xEF, 0xBF, 0xBD, 0x41, 0xC2, 0xA5]
|
||||
},
|
||||
{
|
||||
"input": "A\uDF06",
|
||||
"read": 2,
|
||||
"destinationLength": 4,
|
||||
"written": [0x41, 0xEF, 0xBF, 0xBD]
|
||||
},
|
||||
{
|
||||
"input": "¥¥",
|
||||
"read": 2,
|
||||
"destinationLength": 4,
|
||||
"written": [0xC2, 0xA5, 0xC2, 0xA5]
|
||||
}
|
||||
].forEach(testData => {
|
||||
[
|
||||
{
|
||||
"bufferIncrease": 0,
|
||||
"destinationOffset": 0,
|
||||
"filler": 0
|
||||
},
|
||||
{
|
||||
"bufferIncrease": 10,
|
||||
"destinationOffset": 4,
|
||||
"filler": 0
|
||||
},
|
||||
{
|
||||
"bufferIncrease": 0,
|
||||
"destinationOffset": 0,
|
||||
"filler": 0x80
|
||||
},
|
||||
{
|
||||
"bufferIncrease": 10,
|
||||
"destinationOffset": 4,
|
||||
"filler": 0x80
|
||||
},
|
||||
{
|
||||
"bufferIncrease": 0,
|
||||
"destinationOffset": 0,
|
||||
"filler": "random"
|
||||
},
|
||||
{
|
||||
"bufferIncrease": 10,
|
||||
"destinationOffset": 4,
|
||||
"filler": "random"
|
||||
}
|
||||
].forEach(destinationData => {
|
||||
["ArrayBuffer", "SharedArrayBuffer"].forEach(arrayBufferOrSharedArrayBuffer => {
|
||||
test(() => {
|
||||
// Setup
|
||||
const bufferLength = testData.destinationLength + destinationData.bufferIncrease;
|
||||
const destinationOffset = destinationData.destinationOffset;
|
||||
const destinationLength = testData.destinationLength;
|
||||
const destinationFiller = destinationData.filler;
|
||||
const encoder = new TextEncoder();
|
||||
const buffer = createBuffer(arrayBufferOrSharedArrayBuffer, bufferLength);
|
||||
const view = new Uint8Array(buffer, destinationOffset, destinationLength);
|
||||
const fullView = new Uint8Array(buffer);
|
||||
const control = new Array(bufferLength);
|
||||
let byte = destinationFiller;
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
if (destinationFiller === "random") {
|
||||
byte = Math.floor(Math.random() * 256);
|
||||
}
|
||||
control[i] = byte;
|
||||
fullView[i] = byte;
|
||||
}
|
||||
|
||||
// It's happening
|
||||
const result = encoder.encodeInto(testData.input, view);
|
||||
|
||||
// Basics
|
||||
assert_equals(view.byteLength, destinationLength);
|
||||
assert_equals(view.length, destinationLength);
|
||||
|
||||
// Remainder
|
||||
assert_equals(result.read, testData.read);
|
||||
assert_equals(result.written, testData.written.length);
|
||||
for (let i = 0; i < bufferLength; i++) {
|
||||
if (i < destinationOffset || i >= (destinationOffset + testData.written.length)) {
|
||||
assert_equals(fullView[i], control[i]);
|
||||
} else {
|
||||
assert_equals(fullView[i], testData.written[i - destinationOffset]);
|
||||
}
|
||||
}
|
||||
}, "encodeInto() into " + arrayBufferOrSharedArrayBuffer + " with " + testData.input + " and destination length " + testData.destinationLength + ", offset " + destinationData.destinationOffset + ", filler " + destinationData.filler);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
["DataView",
|
||||
"Int8Array",
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Uint16Array",
|
||||
"Uint32Array",
|
||||
"Uint8ClampedArray",
|
||||
"BigInt64Array",
|
||||
"BigUint64Array",
|
||||
"Float16Array",
|
||||
"Float32Array",
|
||||
"Float64Array"].forEach(type => {
|
||||
["ArrayBuffer", "SharedArrayBuffer"].forEach((arrayBufferOrSharedArrayBuffer) => {
|
||||
test(() => {
|
||||
const viewInstance = new self[type](createBuffer(arrayBufferOrSharedArrayBuffer, 0));
|
||||
assert_throws_js(TypeError, () => new TextEncoder().encodeInto("", viewInstance));
|
||||
}, "Invalid encodeInto() destination: " + type + ", backed by: " + arrayBufferOrSharedArrayBuffer);
|
||||
});
|
||||
});
|
||||
|
||||
["ArrayBuffer", "SharedArrayBuffer"].forEach((arrayBufferOrSharedArrayBuffer) => {
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new TextEncoder().encodeInto("", createBuffer(arrayBufferOrSharedArrayBuffer, 10)));
|
||||
}, "Invalid encodeInto() destination: " + arrayBufferOrSharedArrayBuffer);
|
||||
});
|
||||
|
||||
test(() => {
|
||||
const buffer = new ArrayBuffer(10),
|
||||
view = new Uint8Array(buffer);
|
||||
let { read, written } = new TextEncoder().encodeInto("", view);
|
||||
assert_equals(read, 0);
|
||||
assert_equals(written, 0);
|
||||
new MessageChannel().port1.postMessage(buffer, [buffer]);
|
||||
({ read, written } = new TextEncoder().encodeInto("", view));
|
||||
assert_equals(read, 0);
|
||||
assert_equals(written, 0);
|
||||
({ read, written } = new TextEncoder().encodeInto("test", view));
|
||||
assert_equals(read, 0);
|
||||
assert_equals(written, 0);
|
||||
}, "encodeInto() and a detached output buffer");
|
Loading…
Add table
Reference in a new issue