Skip to content

Commit

Permalink
[1.11>master] [MERGE #6122 @MikeHolman] May 2019 Security Update
Browse files Browse the repository at this point in the history
Merge pull request #6122 from pr/MikeHolman/servicing/1905

May 2019 Security Update that addresses the following issues in ChakraCore:

CVE-2019-0911
CVE-2019-0912
CVE-2019-0913
CVE-2019-0914
CVE-2019-0915
CVE-2019-0916
CVE-2019-0917
CVE-2019-0922
CVE-2019-0923
CVE-2019-0924
CVE-2019-0925
CVE-2019-0927
CVE-2019-0933
CVE-2019-0937
  • Loading branch information
MikeHolman committed May 14, 2019
2 parents 503294e + d797e3f commit 1c75087
Show file tree
Hide file tree
Showing 16 changed files with 181 additions and 35 deletions.
2 changes: 1 addition & 1 deletion lib/Backend/BackwardPass.cpp
Expand Up @@ -4847,7 +4847,7 @@ BackwardPass::ProcessNewScObject(IR::Instr* instr)

// The instruction could have a lazy bailout associated with it, which might get cleared
// later, so we make sure that we only process instructions with the right bailout kind.
if (instr->HasBailOutInfo() && instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck)
if (instr->HasBailOutInfo() && instr->GetBailOutKindNoBits() == IR::BailOutFailedCtorGuardCheck)
{
Assert(instr->IsProfiledInstr());
Assert(instr->GetDst()->IsRegOpnd());
Expand Down
7 changes: 7 additions & 0 deletions lib/Backend/GlobOpt.cpp
Expand Up @@ -13514,6 +13514,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
const bool useValueTypes = !IsLoopPrePass(); // Source value types are not guaranteed to be correct in a loop prepass
switch(instr->m_opcode)
{
case Js::OpCode::StElemC:
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
{
Expand Down Expand Up @@ -13564,8 +13565,13 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
}
break;

case Js::OpCode::ConsoleScopedStFld:
case Js::OpCode::ConsoleScopedStFldStrict:
case Js::OpCode::ScopedStFld:
case Js::OpCode::ScopedStFldStrict:
case Js::OpCode::StFld:
case Js::OpCode::StFldStrict:
case Js::OpCode::StSuperFld:
{
Assert(instr->GetDst());

Expand Down Expand Up @@ -13777,6 +13783,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
break;

case Js::OpCode::NewScObjectNoCtor:
case Js::OpCode::NewScObjectNoCtorFull:
if(doNativeArrayTypeSpec)
{
// Class/object construction can make something a prototype
Expand Down
9 changes: 8 additions & 1 deletion lib/Backend/GlobOptArrays.cpp
Expand Up @@ -1746,7 +1746,14 @@ void GlobOpt::ArraySrcOpt::Optimize()
{
if (newBaseValueType != baseValueType)
{
UpdateValue(nullptr, nullptr, nullptr);
if (globOpt->IsSafeToTransferInPrePass(baseOpnd, baseValue))
{
UpdateValue(nullptr, nullptr, nullptr);
}
else if (isLikelyJsArray && globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues())
{
globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true);
}
}

// For javascript arrays and objects with javascript arrays:
Expand Down
8 changes: 8 additions & 0 deletions lib/Backend/GlobOptBailOut.cpp
Expand Up @@ -1499,6 +1499,14 @@ GlobOpt::MayNeedBailOnImplicitCall(IR::Instr const * instr, Value const * src1Va
);
}

case Js::OpCode::NewScObjectNoCtor:
if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
{
// No helper call with this bailout.
return false;
}
break;

default:
break;
}
Expand Down
8 changes: 8 additions & 0 deletions lib/Backend/GlobOptExpr.cpp
Expand Up @@ -814,20 +814,28 @@ GlobOpt::ProcessArrayValueKills(IR::Instr *instr)
{
switch (instr->m_opcode)
{
case Js::OpCode::StElemC:
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
case Js::OpCode::DeleteElemI_A:
case Js::OpCode::DeleteElemIStrict_A:
case Js::OpCode::ConsoleScopedStFld:
case Js::OpCode::ConsoleScopedStFldStrict:
case Js::OpCode::ScopedStFld:
case Js::OpCode::ScopedStFldStrict:
case Js::OpCode::StFld:
case Js::OpCode::StRootFld:
case Js::OpCode::StFldStrict:
case Js::OpCode::StRootFldStrict:
case Js::OpCode::StSuperFld:
case Js::OpCode::StSlot:
case Js::OpCode::StSlotChkUndecl:
case Js::OpCode::DeleteFld:
case Js::OpCode::DeleteRootFld:
case Js::OpCode::DeleteFldStrict:
case Js::OpCode::DeleteRootFldStrict:
case Js::OpCode::ScopedDeleteFld:
case Js::OpCode::ScopedDeleteFldStrict:
case Js::OpCode::StArrViewElem:
// These array helpers may change A.length (and A[i] could be A.length)...
case Js::OpCode::InlineArrayPush:
Expand Down
58 changes: 51 additions & 7 deletions lib/Backend/GlobOptFields.cpp
Expand Up @@ -327,6 +327,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
IR::JnHelperMethod fnHelper;
switch(instr->m_opcode)
{
case Js::OpCode::StElemC:
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
Assert(dstOpnd != nullptr);
Expand Down Expand Up @@ -358,6 +359,8 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::DeleteRootFld:
case Js::OpCode::DeleteFldStrict:
case Js::OpCode::DeleteRootFldStrict:
case Js::OpCode::ScopedDeleteFld:
case Js::OpCode::ScopedDeleteFldStrict:
sym = instr->GetSrc1()->AsSymOpnd()->m_sym;
KillLiveFields(sym->AsPropertySym(), bv);
if (inGlobOpt)
Expand All @@ -379,13 +382,36 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
this->KillAllObjectTypes(bv);
}
break;

case Js::OpCode::ConsoleScopedStFld:
case Js::OpCode::ConsoleScopedStFldStrict:
case Js::OpCode::ScopedStFld:
case Js::OpCode::ScopedStFldStrict:
// This is already taken care of for FastFld opcodes

if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
}

// fall through

case Js::OpCode::InitFld:
case Js::OpCode::InitConstFld:
case Js::OpCode::InitLetFld:
case Js::OpCode::InitRootFld:
case Js::OpCode::InitRootConstFld:
case Js::OpCode::InitRootLetFld:
#if !FLOATVAR
case Js::OpCode::StSlotBoxTemp:
#endif
case Js::OpCode::StFld:
case Js::OpCode::StRootFld:
case Js::OpCode::StFldStrict:
case Js::OpCode::StRootFldStrict:
case Js::OpCode::StSlot:
case Js::OpCode::StSlotChkUndecl:
case Js::OpCode::StSuperFld:
Assert(dstOpnd != nullptr);
sym = dstOpnd->AsSymOpnd()->m_sym;
if (inGlobOpt)
Expand All @@ -407,11 +433,19 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo

case Js::OpCode::InlineArrayPush:
case Js::OpCode::InlineArrayPop:
KillLiveFields(this->lengthEquivBv, bv);
if (inGlobOpt)
if(instr->m_func->GetThisOrParentInlinerHasArguments())
{
// Deleting an item, or pushing a property to a non-array, may change object layout
KillAllObjectTypes(bv);
this->KillAllFields(bv);
this->SetAnyPropertyMayBeWrittenTo();
}
else
{
KillLiveFields(this->lengthEquivBv, bv);
if (inGlobOpt)
{
// Deleting an item, or pushing a property to a non-array, may change object layout
KillAllObjectTypes(bv);
}
}
break;

Expand All @@ -436,14 +470,23 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
// Kill length field for built-ins that can update it.
if (nullptr != this->lengthEquivBv)
{
KillLiveFields(this->lengthEquivBv, bv);
// If has arguments, all fields are killed in fall through
if (!instr->m_func->GetThisOrParentInlinerHasArguments())
{
KillLiveFields(this->lengthEquivBv, bv);
}
}
// fall through

case IR::JnHelperMethod::HelperArray_Reverse:
// Deleting an item may change object layout
if (inGlobOpt)
if (instr->m_func->GetThisOrParentInlinerHasArguments())
{
this->KillAllFields(bv);
this->SetAnyPropertyMayBeWrittenTo();
}
else if (inGlobOpt)
{
// Deleting an item may change object layout
KillAllObjectTypes(bv);
}
break;
Expand Down Expand Up @@ -484,6 +527,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::InitClass:
case Js::OpCode::InitProto:
case Js::OpCode::NewScObjectNoCtor:
case Js::OpCode::NewScObjectNoCtorFull:
if (inGlobOpt)
{
// Opcodes that make an object into a prototype may break object-header-inlining and final type opt.
Expand Down
4 changes: 2 additions & 2 deletions lib/Backend/IRBuilder.cpp
Expand Up @@ -1674,7 +1674,7 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
}

case Js::OpCode::NewScObjectSimple:
dstValueType = ValueType::GetObject(ObjectType::Object);
dstValueType = ValueType::GetObject(ObjectType::UninitializedObject);
// fall-through
case Js::OpCode::LdFuncExpr:
m_func->DisableCanDoInlineArgOpt();
Expand Down Expand Up @@ -4992,7 +4992,7 @@ IRBuilder::BuildAuxiliary(Js::OpCode newOpcode, uint32 offset)
// lower take it from there...
srcOpnd = IR::IntConstOpnd::New(auxInsn->Offset, TyUint32, m_func);
dstOpnd = this->BuildDstOpnd(dstRegSlot);
dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Object));
dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);

// Because we're going to be making decisions based off the value, we have to defer
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/Inline.cpp
Expand Up @@ -4501,6 +4501,8 @@ Inline::SplitConstructorCallCommon(
{
createObjInstr->SetByteCodeOffset(newObjInstr);
createObjInstr->GetSrc1()->SetIsJITOptimizedReg(true);
// We're splitting a single byte code, so the interpreter has to resume from the beginning if we bail out.
createObjInstr->forcePreOpBailOutIfNeeded = true;
newObjInstr->InsertBefore(createObjInstr);

createObjDst->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
Expand Down
47 changes: 33 additions & 14 deletions lib/Backend/Lower.cpp
Expand Up @@ -4580,18 +4580,40 @@ Lowerer::LowerNewScObject(IR::Instr *newObjInstr, bool callCtor, bool hasArgs, b
{
Assert(!newObjDst->CanStoreTemp());
// createObjDst = NewScObject...(ctorOpnd)
newScHelper = !callCtor ?
(isBaseClassConstructorNewScObject ?
(hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) :
(hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor)) :
(hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg);

LoadScriptContext(newObjInstr);
m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1());

newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func);
newObjInstr->InsertBefore(newScObjCall);
m_lowererMD.LowerCall(newScObjCall, 0);
if (callCtor)
{
newScHelper = (hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg);

m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1());

newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func);
newObjInstr->InsertBefore(newScObjCall);
m_lowererMD.LowerCall(newScObjCall, 0);
}
else
{
newScHelper =
(isBaseClassConstructorNewScObject ?
(hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) :
(hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor));

// Branch around the helper call to execute the inlined ctor.
Assert(callCtorLabel != nullptr);
newObjInstr->InsertAfter(callCtorLabel);

// Change the NewScObject* to a helper call on the spot. This generates implicit call bailout for us if we need one.
m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->UnlinkSrc1());
m_lowererMD.ChangeToHelperCall(newObjInstr, newScHelper);

// Then we're done.
Assert(createObjDst == newObjDst);

// Return the first instruction above the region we've just lowered.
return RemoveLoweredRegionStartMarker(startMarkerInstr);
}
}
}

Expand Down Expand Up @@ -4836,21 +4858,18 @@ bool Lowerer::TryLowerNewScObjectWithFixedCtorCache(IR::Instr* newObjInstr, IR::
skipNewScObj = false;
returnNewScObj = false;

AssertMsg(!PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func) || !newObjInstr->HasBailOutInfo(),
"Why do we have bailout on NewScObject when ObjTypeSpecNewObj is off?");

if (PHASE_OFF(Js::FixedNewObjPhase, newObjInstr->m_func) && PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func))
{
return false;
}

JITTimeConstructorCache * ctorCache;

if (newObjInstr->HasBailOutInfo() && !newObjInstr->HasLazyBailOut())
if (newObjInstr->HasBailOutInfo() && !newObjInstr->HasLazyBailOut() && newObjInstr->GetBailOutKindNoBits() == IR::BailOutFailedCtorGuardCheck)
{
Assert(newObjInstr->IsNewScObjectInstr());
Assert(newObjInstr->IsProfiledInstr());
Assert(newObjInstr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck || newObjInstr->HasLazyBailOut());
Assert(newObjInstr->GetBailOutKindNoBits() == IR::BailOutFailedCtorGuardCheck || newObjInstr->HasLazyBailOut());

emitBailOut = true;

Expand Down
11 changes: 11 additions & 0 deletions lib/Runtime/ByteCode/ByteCodeEmitter.cpp
Expand Up @@ -3636,6 +3636,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
#if ENABLE_TTD
&& !funcInfo->GetParsedFunctionBody()->GetScriptContext()->GetThreadContext()->IsRuntimeInTTDMode()
#endif
&& !funcInfo->byteCodeFunction->IsCoroutine()
);

if (funcInfo->GetHasCachedScope())
Expand Down Expand Up @@ -4051,6 +4052,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch)
sym->SetIsGlobalCatch(true);
}

if (sym->NeedsScopeObject())
{
scope->SetIsObject();
}

Assert(sym->GetScopeSlot() == Js::Constants::NoProperty);
if (sym->NeedsSlotAlloc(this, funcInfo))
{
Expand All @@ -4071,6 +4077,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch)
sym->SetIsGlobalCatch(true);
}

if (sym->NeedsScopeObject())
{
scope->SetIsObject();
}

if (scope->GetMustInstantiate())
{
if (sym->IsInSlot(this, funcInfo))
Expand Down
12 changes: 6 additions & 6 deletions lib/Runtime/ByteCode/ByteCodeGenerator.cpp
Expand Up @@ -119,10 +119,10 @@ void EndVisitBlock(ParseNodeBlock *pnode, ByteCodeGenerator *byteCodeGenerator)
Scope *scope = pnode->scope;
FuncInfo *func = scope->GetFunc();

if (!byteCodeGenerator->IsInDebugMode() &&
scope->HasInnerScopeIndex())
if (!(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine())
&& scope->HasInnerScopeIndex())
{
// In debug mode, don't release the current index, as we're giving each scope a unique index, regardless
// In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index, regardless
// of nesting.
Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
func->ReleaseInnerScopeIndex();
Expand Down Expand Up @@ -155,12 +155,12 @@ void BeginVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
void EndVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
{
Scope *scope = pnode->AsParseNodeCatch()->scope;
FuncInfo *func = scope->GetFunc();

if (scope->HasInnerScopeIndex() && !byteCodeGenerator->IsInDebugMode())
if (scope->HasInnerScopeIndex() && !(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine()))
{
// In debug mode, don't release the current index, as we're giving each scope a unique index,
// In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index,
// regardless of nesting.
FuncInfo *func = scope->GetFunc();

Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
func->ReleaseInnerScopeIndex();
Expand Down
4 changes: 2 additions & 2 deletions lib/Runtime/ByteCode/OpCodes.h
Expand Up @@ -578,8 +578,8 @@ MACRO_EXTEND_WMS_AND_PROFILED(NewScObjectSpread, CallIExtended, OpSideEffect|O
MACRO_WMS_PROFILED2( NewScObjArray, CallI, OpSideEffect|OpUseAllFields|OpCallInstr) // Create new ScriptObject instance
MACRO_WMS_PROFILED2( NewScObjArraySpread, CallIExtended, OpSideEffect|OpUseAllFields|OpCallInstr) // Create new ScriptObject instance
MACRO( NewScObject_A, Auxiliary, OpSideEffect|OpUseAllFields) // Create new ScriptObject instance passing only constants
MACRO_WMS( NewScObjectNoCtorFull, Reg2, OpTempObjectCanStoreTemp) // Create new object that will be used for the 'this' binding in a base class constructor
MACRO_BACKEND_ONLY( NewScObjectNoCtor, Empty, OpTempObjectCanStoreTemp) // Create new object that will be passed into a constructor
MACRO_WMS( NewScObjectNoCtorFull, Reg2, OpTempObjectCanStoreTemp|OpHasImplicitCall) // Create new object that will be used for the 'this' binding in a base class constructor
MACRO_BACKEND_ONLY( NewScObjectNoCtor, Empty, OpTempObjectCanStoreTemp|OpHasImplicitCall) // Create new object that will be passed into a constructor
MACRO_BACKEND_ONLY( GetNewScObject, Empty, OpTempObjectTransfer) // Determine which object to finally use as the result of NewScObject (object passed into constructor as 'this', or object returned by constructor)
MACRO_BACKEND_ONLY( UpdateNewScObjectCache, Empty, None) // Update the cache used for NewScObject
MACRO_WMS( NewScObjectSimple, Reg1, OpTempObjectCanStoreTemp)
Expand Down

0 comments on commit 1c75087

Please sign in to comment.