Microsoft Edge Chakra - Buffer Overflow

EDB-ID: 42466
Author: Huang Anwen
Published: 2017-08-17
CVE: CVE-2017-8636
Type: Dos
Platform: Windows
Vulnerable App: N/A

 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();
}
}
Related Posts