From 18dc5fa50baca4a59510fe5a08ed27d5076adaf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marja=20H=C3=B6ltt=C3=A4?= Date: Mon, 28 Nov 2022 15:35:04 +0100 Subject: [PATCH] [rab/gsab] Improve test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Parameter conversions resizing the underlying buffer but *not* in a way that would make a length-tracking TA go OOB. 2) Special case of the former: resize to 0. Bug: v8:11111, chromium:1392577, chromium:1393375 Change-Id: Ia0f54fc4530618fa2313737d6c643abdb24b9a36 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4055630 Reviewed-by: Jakob Kummerow Commit-Queue: Marja Hölttä Cr-Commit-Position: refs/heads/main@{#84519} --- .../typedarray-resizablearraybuffer.js | 593 +++++++++++++++++- 1 file changed, 586 insertions(+), 7 deletions(-) diff --git a/test/mjsunit/typedarray-resizablearraybuffer.js b/test/mjsunit/typedarray-resizablearraybuffer.js index b07e6f4535..f1d1fd34e1 100644 --- a/test/mjsunit/typedarray-resizablearraybuffer.js +++ b/test/mjsunit/typedarray-resizablearraybuffer.js @@ -1286,6 +1286,68 @@ TestFill(ArrayFillHelper, false); assertThrows( () => { TypedArrayFillHelper(fixedLength, 3, 1, evil); }, TypeError); } + // Resizing + a length-tracking TA -> no OOB, but bounds recomputation needed. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 1; + }}; + TypedArrayFillHelper(lengthTracking, evil, 0, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 0; + }}; + TypedArrayFillHelper(lengthTracking, 1, evil, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 4; + }}; + TypedArrayFillHelper(lengthTracking, 1, 0, evil); + } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 1; + }}; + TypedArrayFillHelper(lengthTracking, evil, 0, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 0; + }}; + TypedArrayFillHelper(lengthTracking, 1, evil, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 4; + }}; + TypedArrayFillHelper(lengthTracking, 1, 0, evil); + } })(); (function ArrayFillParameterConversionResizes() { @@ -1298,6 +1360,8 @@ TestFill(ArrayFillHelper, false); rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 3; }}; ArrayFillHelper(fixedLength, evil, 1, 2); + // The underlying data doesn't change: all writes fail because 'fixedLength' + // is OOB. assertEquals([0, 0], ReadDataFromBuffer(rab, ctor)); } for (let ctor of ctors) { @@ -1322,6 +1386,71 @@ TestFill(ArrayFillHelper, false); ArrayFillHelper(fixedLength, 3, 1, evil); assertEquals([0, 0], ReadDataFromBuffer(rab, ctor)); } + // Resizing + a length-tracking TA -> no OOB, but bounds recomputation needed. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 3; + }}; + ArrayFillHelper(lengthTracking, evil, 0, 4); + assertEquals([3, 3], ReadDataFromBuffer(rab, ctor)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 0; + }}; + ArrayFillHelper(lengthTracking, 3, evil, 4); + assertEquals([3, 3], ReadDataFromBuffer(rab, ctor)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 4; + }}; + ArrayFillHelper(lengthTracking, 3, 0, evil); + assertEquals([3, 3], ReadDataFromBuffer(rab, ctor)); + } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 3; + }}; + ArrayFillHelper(lengthTracking, evil, 0, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 0; + }}; + ArrayFillHelper(lengthTracking, 3, evil, 4); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return 4; + }}; + ArrayFillHelper(lengthTracking, 3, 0, evil); + } })(); function At(atHelper, oobThrows) { @@ -1389,16 +1518,56 @@ function AtParameterConversionResizes(atHelper) { 8 * ctor.BYTES_PER_ELEMENT); const fixedLength = new ctor(rab, 0, 4); - let evil = { valueOf: () => { rab.resize(2); return 0;}}; + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 0; + }}; assertEquals(undefined, atHelper(fixedLength, evil)); } - + // Resizing + a length-tracking TA -> no OOB, but bounds recomputation needed. for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); const lengthTracking = new ctor(rab); - let evil = { valueOf: () => { rab.resize(2); return -1;}}; + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return -1; + }}; + // The TypedArray is *not* out of bounds since it's length-tracking. + assertEquals(undefined, atHelper(lengthTracking, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + WriteToTypedArray(lengthTracking, 0, 25); + + const evil = { valueOf: () => { + rab.resize(2 * ctor.BYTES_PER_ELEMENT); return 0; + }}; + // The TypedArray is *not* out of bounds since it's length-tracking. + assertEquals(25, atHelper(lengthTracking, evil)); + } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + const evil = { valueOf: () => { + rab.resize(0); return -1; + }}; + // The TypedArray is *not* out of bounds since it's length-tracking. + assertEquals(undefined, atHelper(lengthTracking, evil)); + } + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + WriteToTypedArray(lengthTracking, 0, 25); + + const evil = { valueOf: () => { + rab.resize(0); return 0; + }}; // The TypedArray is *not* out of bounds since it's length-tracking. assertEquals(undefined, atHelper(lengthTracking, evil)); } @@ -1491,6 +1660,7 @@ AtParameterConversionResizes(ArrayAtHelper); assertThrows(() => { fixedLength.slice(evil); }, TypeError); assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength); } + // Resizing + a length-tracking TA -> no OOB, but bounds recomputation needed. for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); @@ -1503,6 +1673,19 @@ AtParameterConversionResizes(ArrayAtHelper); assertEquals([1, 2, 0, 0], ToNumbers(lengthTracking.slice(evil))); assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i + 1); + } + const evil = { valueOf: () => { rab.resize(0); + return 0; }}; + assertEquals([0, 0, 0, 0], ToNumbers(lengthTracking.slice(evil))); + assertEquals(0, rab.byteLength); + } })(); function SliceParameterConversionGrows(sliceHelper) { @@ -1812,6 +1995,7 @@ TestCopyWithin(ArrayCopyWithinHelper, false); lengthTracking.copyWithin(evil, 0); assertEquals([0, 1, 0], ToNumbers(lengthTracking)); } + // Resizing + a length-tracking TA -> no OOB, but bounds recomputation needed. for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); @@ -1829,6 +2013,23 @@ TestCopyWithin(ArrayCopyWithinHelper, false); lengthTracking.copyWithin(0, evil); assertEquals([2, 1, 2], ToNumbers(lengthTracking)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + // [0, 1, 2, 3] + // ^ + // start + // ^ + // target + const evil = { valueOf: () => { rab.resize(0); return 2; }}; + lengthTracking.copyWithin(0, evil); + assertEquals([], ToNumbers(lengthTracking)); + } })(); (function CopyWithinParameterConversionGrows() { @@ -2229,7 +2430,7 @@ function EntriesKeysValuesShrinkMidIteration( return rab; } - // Iterating with entries() (the 4 loops below). + // Iterating with entries() (the 5 loops below). for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const fixedLength = new ctor(rab, 0, 4); @@ -2252,6 +2453,7 @@ function EntriesKeysValuesShrinkMidIteration( rab, 1, 3 * ctor.BYTES_PER_ELEMENT); }); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -2261,6 +2463,16 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + + TestIterationAndResize(entriesHelper(lengthTracking), + [[0, 0]], + rab, 1, 0); + } + for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -2270,7 +2482,7 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); } - // Iterating with keys() (the 4 loops below). + // Iterating with keys() (the 5 loops below). for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const fixedLength = new ctor(rab, 0, 4); @@ -2293,6 +2505,7 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); }); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -2302,6 +2515,16 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + + TestIterationAndResize(keysHelper(lengthTracking), + [0], + rab, 1, 0); + } + for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -2311,7 +2534,7 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); } - // Iterating with values() (the 4 loops below). + // Iterating with values() (the 5 loops below). for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const fixedLength = new ctor(rab, 0, 4); @@ -2334,6 +2557,7 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); }); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -2343,6 +2567,16 @@ function EntriesKeysValuesShrinkMidIteration( rab, 2, 3 * ctor.BYTES_PER_ELEMENT); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + + TestIterationAndResize(valuesHelper(lengthTracking), + [0, 2], + rab, 2, 0); + } + for (let ctor of ctors) { const rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -2593,6 +2827,7 @@ function EveryShrinkMidIteration(everyHelper, hasUndefined) { } } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -2607,6 +2842,21 @@ function EveryShrinkMidIteration(everyHelper, hasUndefined) { } } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertTrue(everyHelper(lengthTracking, CollectValuesAndResize)); + if (hasUndefined) { + assertEquals([0, 2, undefined, undefined], values); + } else { + assertEquals([0, 2], values); + } + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -2761,6 +3011,7 @@ function SomeShrinkMidIteration(someHelper, hasUndefined) { } } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -2775,6 +3026,21 @@ function SomeShrinkMidIteration(someHelper, hasUndefined) { } } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertFalse(someHelper(lengthTracking, CollectValuesAndResize)); + if (hasUndefined) { + assertEquals([0, 2, undefined, undefined], values); + } else { + assertEquals([0, 2], values); + } + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -3171,6 +3437,7 @@ function FindShrinkMidIteration(findHelper) { assertEquals([4, undefined], values); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -3181,6 +3448,17 @@ function FindShrinkMidIteration(findHelper) { assertEquals([0, 2, 4, undefined], values); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertEquals(undefined, findHelper(lengthTracking, CollectValuesAndResize)); + assertEquals([0, 2, undefined, undefined], values); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -3326,6 +3604,7 @@ function FindIndexShrinkMidIteration(findIndexHelper) { assertEquals([4, undefined], values); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -3336,6 +3615,17 @@ function FindIndexShrinkMidIteration(findIndexHelper) { assertEquals([0, 2, 4, undefined], values); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertEquals(-1, findIndexHelper(lengthTracking, CollectValuesAndResize)); + assertEquals([0, 2, undefined, undefined], values); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -3479,6 +3769,7 @@ function FindLastShrinkMidIteration(findLastHelper) { assertEquals([6, undefined], values); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -3490,6 +3781,18 @@ function FindLastShrinkMidIteration(findLastHelper) { assertEquals([6, 4, 2, 0], values); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertEquals(undefined, + findLastHelper(lengthTracking, CollectValuesAndResize)); + assertEquals([6, 4, undefined, undefined], values); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -3649,6 +3952,7 @@ function FindLastIndexShrinkMidIteration(findLastIndexHelper) { assertEquals([6, 4, 2, 0], values); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -3660,6 +3964,18 @@ function FindLastIndexShrinkMidIteration(findLastIndexHelper) { assertEquals([6, undefined, 2, 0], values); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 1; + resizeTo = 0; + assertEquals(-1, + findLastIndexHelper(lengthTracking, CollectValuesAndResize)); + assertEquals([6, undefined, undefined, undefined], values); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -3910,6 +4226,7 @@ Filter(ArrayFilterHelper, false); assertEquals([4, undefined], values); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -3920,6 +4237,17 @@ Filter(ArrayFilterHelper, false); assertEquals([0, 2, 4, undefined], values); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + values = []; + resizeAfter = 2; + resizeTo = 0; + assertEquals([], ToNumbers(lengthTracking.filter(CollectValuesAndResize))); + assertEquals([0, 2, undefined, undefined], values); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -4199,6 +4527,7 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([4, undefined], ForEachHelper(fixedLengthWithOffset)); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -4207,6 +4536,15 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([0, 2, 4, undefined], ForEachHelper(lengthTracking)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 0; + assertEquals([0, 2, undefined, undefined], ForEachHelper(lengthTracking)); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -4233,6 +4571,7 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([4, undefined], ReduceHelper(fixedLengthWithOffset)); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -4241,6 +4580,15 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([0, 2, 4, undefined], ReduceHelper(lengthTracking)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 0; + assertEquals([0, 2, undefined, undefined], ReduceHelper(lengthTracking)); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -4267,6 +4615,7 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([6, undefined], ReduceRightHelper(fixedLengthWithOffset)); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -4276,6 +4625,15 @@ ForEachReduceReduceRight(ArrayForEachHelper, ArrayReduceHelper, assertEquals([6, 4, 2, 0], ReduceRightHelper(lengthTracking)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 2; + resizeTo = 0; + assertEquals([6, 4, undefined, undefined], ReduceRightHelper(lengthTracking)); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTracking = new ctor(rab, 0); @@ -4621,6 +4979,7 @@ function IncludesParameterConversionResizes(helper) { assertFalse(helper(fixedLength, 0, evil)); } + // Resizing + a length-tracking TA -> no OOB. for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); @@ -4635,6 +4994,21 @@ function IncludesParameterConversionResizes(helper) { assertTrue(helper(lengthTracking, undefined, evil)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + let evil = { valueOf: () => { + rab.resize(0); + return 0; + }}; + assertFalse(helper(lengthTracking, undefined)); + // "includes" iterates until the original length and sees "undefined"s. + assertTrue(helper(lengthTracking, undefined, evil)); + } + for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); @@ -4950,6 +5324,41 @@ function IndexOfParameterConversionShrinks(indexOfHelper, lastIndexOfHelper) { // 2 no longer found. assertEquals(-1, indexOfHelper(lengthTracking, 2, evil)); } + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + + let evil = { valueOf: () => { + rab.resize(0); + return 2; + }}; + assertEquals(2, indexOfHelper(lengthTracking, 2)); + // 2 no longer found. + assertEquals(-1, indexOfHelper(lengthTracking, evil)); + } + + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + + let evil = { valueOf: () => { + rab.resize(0); + return 1; + }}; + assertEquals(2, indexOfHelper(lengthTracking, 2)); + // 2 no longer found. + assertEquals(-1, indexOfHelper(lengthTracking, 2, evil)); + } } IndexOfParameterConversionShrinks(TypedArrayIndexOfHelper); IndexOfParameterConversionShrinks(ArrayIndexOfHelper); @@ -5001,7 +5410,25 @@ function LastIndexOfParameterConversionShrinks(lastIndexOfHelper) { // 2 no longer found. assertEquals(-1, lastIndexOfHelper(lengthTracking, 2, evil)); } - // Test resizing to 0 separately since it's special. + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(lengthTracking, i, i); + } + + const evil = { valueOf: () => { + rab.resize(0); + return 2; + }}; + assertEquals(2, lastIndexOfHelper(lengthTracking, 2)); + // 2 no longer found. + assertEquals(-1, lastIndexOfHelper(lengthTracking, evil)); + } + for (let ctor of ctors) { const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, 8 * ctor.BYTES_PER_ELEMENT); @@ -5275,6 +5702,21 @@ function JoinParameterConversionShrinks(joinHelper) { // the new length are converted to the empty string. assertEquals('0.0..', joinHelper(lengthTracking, evil)); } + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + let evil = { toString: () => { + rab.resize(0); + return '.'; + }}; + // We iterate 4 elements, since it was the starting length. All elements are + // converted to the empty string. + assertEquals('...', joinHelper(lengthTracking, evil)); + } } JoinParameterConversionShrinks(TypedArrayJoinHelper); JoinParameterConversionShrinks(ArrayJoinHelper); @@ -5369,6 +5811,33 @@ function ToLocaleStringNumberPrototypeToLocaleStringShrinks( assertEquals('0,0,,', toLocaleStringHelper(lengthTracking)); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab); + + let resizeAfter = 1; + Number.prototype.toLocaleString = function() { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(0); + } + return oldNumberPrototypeToLocaleString.call(this); + } + BigInt.prototype.toLocaleString = function() { + --resizeAfter; + if (resizeAfter == 0) { + rab.resize(0); + } + return oldBigIntPrototypeToLocaleString.call(this); + } + + // We iterate 4 elements, since it was the starting length. Elements beyond + // the new length are converted to the empty string. + assertEquals('0,,,', toLocaleStringHelper(lengthTracking)); + } + Number.prototype.toLocaleString = oldNumberPrototypeToLocaleString; BigInt.prototype.toLocaleString = oldBigIntPrototypeToLocaleString; } @@ -5636,6 +6105,20 @@ function MapShrinkMidIteration(mapHelper, hasUndefined) { } } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAfter = 1; + resizeTo = 0; + if (hasUndefined) { + assertEquals([0, undefined, undefined, undefined], + Helper(lengthTracking)); + } else { + assertEquals([0], Helper(lengthTracking)); + } + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -5793,6 +6276,33 @@ MapGrowMidIteration(ArrayMapHelper); assertEquals([0, 1, undefined, undefined], Helper(lengthTracking)); assertEquals(2 * ctor.BYTES_PER_ELEMENT, rab.byteLength); } + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + + const taWrite = new ctor(rab); + for (let i = 0; i < 4; ++i) { + WriteToTypedArray(taWrite, i, i); + } + + let resizeWhenConstructorCalled = false; + class MyArray extends ctor { + constructor(...params) { + super(...params); + if (resizeWhenConstructorCalled) { + rab.resize(0); + } + } + }; + + const lengthTracking = new MyArray(rab); + resizeWhenConstructorCalled = true; + assertEquals([undefined, undefined, undefined, undefined], + Helper(lengthTracking)); + assertEquals(0, rab.byteLength); + } })(); (function MapSpeciesCreateGrows() { @@ -6205,6 +6715,16 @@ Reverse(ArrayReverseHelper, false); assertEquals([1, 2, 4], ToNumbers(new ctor(rab))); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeTo = 0; + lengthTracking.set(CreateSourceProxy(1)); + assertEquals([], ToNumbers(lengthTracking)); + assertEquals([], ToNumbers(new ctor(rab))); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -6386,6 +6906,17 @@ Reverse(ArrayReverseHelper, false); assertEquals([1, 1, 4], ToNumbers(new ctor(rab))); } + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab, 0); + resizeAt = 2; + resizeTo = 0; + lengthTracking.set(CreateSourceProxy(2)); + assertEquals([], ToNumbers(lengthTracking)); + assertEquals([], ToNumbers(new ctor(rab))); + } + for (let ctor of ctors) { rab = CreateRabForTest(ctor); const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT); @@ -6858,6 +7389,31 @@ Reverse(ArrayReverseHelper, false); }}; assertThrows(() => { lengthTracking.subarray(0, evil); }); } + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + + let evil = { valueOf: () => { + rab.resize(0); + return 1; + }}; + assertThrows(() => { lengthTracking.subarray(0, evil); }); + } + + // Like the previous test, but now we construct a smaller subarray and it + // succeeds. + for (let ctor of ctors) { + const rab = CreateRabForTest(ctor); + const lengthTracking = new ctor(rab); + + let evil = { valueOf: () => { + rab.resize(0); + return 0; + }}; + assertEquals([], ToNumbers(lengthTracking.subarray(evil, 0))); + } })(); (function SubarrayParameterConversionGrows() { @@ -7363,6 +7919,17 @@ function SortCallbackShrinks(sortHelper) { assertTrue([10, 9, 8, 7].includes(newData[0])); assertTrue([10, 9, 8, 7].includes(newData[1])); } + + for (let ctor of ctors) { + rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + resizeTo = 0; + const lengthTracking = new ctor(rab, 0); + const taFull = new ctor(rab, 0); + WriteUnsortedData(taFull); + + sortHelper(lengthTracking, CustomComparison); + } } SortCallbackShrinks(TypedArraySortHelper); SortCallbackShrinks(ArraySortHelper); @@ -7543,6 +8110,18 @@ SortCallbackGrows(ArraySortHelper); }}; assertThrows(() => { helper(lengthTracking, evil, 8); }, TypeError); } + + // Special case: resizing to 0 -> length-tracking TA still not OOB. + for (let ctor of ctors) { + const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT, + 8 * ctor.BYTES_PER_ELEMENT); + const lengthTracking = new ctor(rab, 0); + const evil = {toString: () => { + rab.resize(0); + return 0; // Index too large after resize. + }}; + assertThrows(() => { helper(lengthTracking, evil, 8); }, TypeError); + } })(); (function ObjectDefinePropertyParameterConversionGrows() {