Exploit-DB #42466
Description: Microsoft Edge Chakra - Buffer Overflow
Type: dos
Platform: windows
Date: 2017-08-17
Author: Huang Anwen

Exploit:
<!--
Report by Huang Anwen, He Xiaoxiao of ichunqiu Ker Team

There is an overflow when constructoring a new object with arguments which has 0xffff elements in Chakra!
This issue can be reproduced steadly in uptodate Edge in Win10 WIP.

//ChakraCore-master\lib\Runtime\ByteCode\ByteCodeEmitter.cpp
void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
{
    Js::ArgSlot argCount = pnode->sxCall.argCount;			//pnode->sxCall.argCount=0xFFFF
    argCount++; // include "this"							//overflow!!!! argCount==0

    BOOL fSideEffectArgs = FALSE;
    unsigned int tmpCount = CountArguments(pnode->sxCall.pnodeArgs, &fSideEffectArgs);
    Assert(argCount == tmpCount);

    if (argCount != (Js::ArgSlot)argCount)
    {
        Js::Throw::OutOfMemory();
    }

    byteCodeGenerator->StartStatement(pnode);

    // Start call, allocate out param space
    funcInfo->StartRecordingOutArgs(argCount);

    // Assign the call target operand(s), putting them into expression temps if necessary to protect
    // them from side-effects.
    if (fSideEffectArgs)
    {
        SaveOpndValue(pnode->sxCall.pnodeTarget, funcInfo);
    }

    if (pnode->sxCall.pnodeTarget->nop == knopSuper)
    {
        EmitSuperFieldPatch(funcInfo, pnode, byteCodeGenerator);
    }

    Emit(pnode->sxCall.pnodeTarget, byteCodeGenerator, funcInfo, false, true);

    if (pnode->sxCall.pnodeArgs == nullptr)
    {
        funcInfo->ReleaseLoc(pnode->sxCall.pnodeTarget);
        Js::OpCode op = (CreateNativeArrays(byteCodeGenerator, funcInfo)
            && CallTargetIsArray(pnode->sxCall.pnodeTarget))
            ? Js::OpCode::NewScObjArray : Js::OpCode::NewScObject;
        Assert(argCount == 1);

        Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(op);
        byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
        byteCodeGenerator->Writer()->CallI(op, funcInfo->AcquireLoc(pnode),
            pnode->sxCall.pnodeTarget->location, argCount, callSiteId);
    }
    else
    {
        byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
        uint32 actualArgCount = 0;

        if (IsCallOfConstants(pnode))
        {
            funcInfo->ReleaseLoc(pnode->sxCall.pnodeTarget);
            actualArgCount = EmitNewObjectOfConstants(pnode, byteCodeGenerator, funcInfo, argCount);
        }
        else
        {
            Js::OpCode op;
            if ((CreateNativeArrays(byteCodeGenerator, funcInfo) && CallTargetIsArray(pnode->sxCall.pnodeTarget)))
            {
                op = pnode->sxCall.spreadArgCount > 0 ? Js::OpCode::NewScObjArraySpread : Js::OpCode::NewScObjArray;
            }
            else
            {
                op = pnode->sxCall.spreadArgCount > 0 ? Js::OpCode::NewScObjectSpread : Js::OpCode::NewScObject;
            }

            Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(op);


            Js::AuxArray<uint32> *spreadIndices = nullptr;
            actualArgCount = EmitArgList(pnode->sxCall.pnodeArgs, Js::Constants::NoRegister, Js::Constants::NoRegister, Js::Constants::NoRegister,
                false, true, byteCodeGenerator, funcInfo, callSiteId, pnode->sxCall.spreadArgCount, &spreadIndices);
            funcInfo->ReleaseLoc(pnode->sxCall.pnodeTarget);


            if (pnode->sxCall.spreadArgCount > 0)
            {
                Assert(spreadIndices != nullptr);
                uint spreadExtraAlloc = spreadIndices->count * sizeof(uint32);
                uint spreadIndicesSize = sizeof(*spreadIndices) + spreadExtraAlloc;
                byteCodeGenerator->Writer()->CallIExtended(op, funcInfo->AcquireLoc(pnode), pnode->sxCall.pnodeTarget->location,
                    (uint16)actualArgCount, Js::CallIExtended_SpreadArgs,
                    spreadIndices, spreadIndicesSize, callSiteId);
            }
            else
            {
                byteCodeGenerator->Writer()->CallI(op, funcInfo->AcquireLoc(pnode), pnode->sxCall.pnodeTarget->location,
                    (uint16)actualArgCount, callSiteId);
            }
        }

        Assert(argCount == actualArgCount);
    }

    // End call, pop param space
    funcInfo->EndRecordingOutArgs(argCount);
    return;
}

//ChakraCore-master\lib\Runtime\Language\InterpreterStackFrame.cpp
inline void InterpreterStackFrame::SetOut(ArgSlot_OneByte outRegisterID, Var aValue)
{
	Assert(m_outParams + outRegisterID < m_outSp);		
	m_outParams[outRegisterID] = aValue;				//OOB Write!!!! outRegisterID could be 0~0xFFFF, but m_outParams has one element only 
}

//ChakraCore-master\lib\Runtime\Language\InterpreterStackFrame.cpp
    Var InterpreterStackFrame::InterpreterHelper(ScriptFunction* function, ArgumentReader args, void* returnAddress, void* addressOfReturnAddress, const bool isAsmJs)
    {

#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
        // Support for simulating partially initialized interpreter stack frame.
        InterpreterThunkStackCountTracker tracker;

        if (CONFIG_ISENABLED(InjectPartiallyInitializedInterpreterFrameErrorFlag) &&
            CONFIG_FLAG(InjectPartiallyInitializedInterpreterFrameError) == InterpreterThunkStackCountTracker::GetCount())
        {
            switch (CONFIG_FLAG(InjectPartiallyInitializedInterpreterFrameErrorType))
            {
            case 0:
                DebugBreak();
                break;
            case 1:
                Js::JavascriptError::MapAndThrowError(function->GetScriptContext(), VBSERR_InternalError);
                break;
            default:
                DebugBreak();
            }
        }
#endif
        ScriptContext* functionScriptContext = function->GetScriptContext();
        ThreadContext * threadContext = functionScriptContext->GetThreadContext();
        Assert(!threadContext->IsDisableImplicitException());
        functionScri