archives

« Bugzilla Issues Index

#1200 — 8.4.2.4: ArraySetLength algorithm can produce broken arrays


The ArraySetLength algorithm can produce arrays which no longer respect the array invariant described in [8.4.2].

test case sketch (most likely only reproducible if you use a custom ES5/6 implementation):
---
function defLen(arr, len, f) {
Object.defineProperty(arr, "length", {value: {valueOf: function(){ f && f(); return len }}});
}

a = [];
defLen(a, 1, function() {defLen(a, 10); a[5]='test'});
---


ArraySetLength retrieves and stores the current 'length' PropertyDescriptor in steps 1-2. When ToUint32() is called in step 5, user defined code can be executed (e.g. a custom 'valueOf' function, see above). This user code may change the array's length again and thereby invalidate the previously stored `oldLen` value. And as a result, both ArraySetLength invocations assume `newLen >= oldLen` (step 8) and no array trimming will be applied.

a = [];
a.[[DefineOwnProperty]]("length", {[[Value: 1 (user code)]]})
-> ArraySetLength(A = a, Desc = {[[Value: 1 (user code)]]})
-> oldLen = 0
-> newLen = ToUint32(Desc.[[Value]]) = 1
/* start user code as a side-effect of ToUint32() */
-> a.[[DefineOwnProperty]]("length", {[[Value: 10]]})
-> ArraySetLength(A = a, Desc = {[[Value: 10]]})
-> oldLen = 0
-> newLen = ToUint32(Desc.[[Value]]) = 10
-> newLen(10) >= oldLen(0) => OrdinaryDefineOwnProperty(A, "length", newLenDesc = {[[Value: 10]]})
-> return
-> a.[[SetP]]("5", 'test', a)
/* end user code as a side-effect of ToUint32() */
-> newLen(1) >= oldLen(0) => OrdinaryDefineOwnProperty(A, "length", newLenDesc = {[[Value: 1]]})

Now `a`'s "length" property will be `1`, but the property at index 5 wasn't removed.


This bug is also present in the ES5.1 specification.


Really good catch! Please find more like this..,

Fixed in rev 14 editor's draft


in Rev 14 draft