archives

« Bugzilla Issues Index

#1262 — 8.4.2.4: ArraySetLength algorithm can produce broken arrays


+++ This bug was initially created as a clone of Bug #1200 +++

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.


I think this has been fixed.

In the current draft (rev 31) the coercion happens in step 4, and doesn't get the array's oldLen until steps 6-9.


(In reply to Jason Orendorff from comment #1)
> I think this has been fixed.

This issue report is for a possible 5.1 errata, ES6 was covered in bug 1200.