Skip to content
Permalink
Browse files

[1.11>master] [MERGE #6155 @pleath] ChakraCore servicing update for J…

…une, 2019

Merge pull request #6155 from pleath:1906

This release addresses the following issues:

CVE-2019-0989
CVE-2019-0990
CVE-2019-0991
CVE-2019-0992
CVE-2019-0993
CVE-2019-1003
CVE-2019-1023
CVE-2019-1024
CVE-2019-1051
CVE-2019-1052
  • Loading branch information...
pleath committed Jun 13, 2019
2 parents e3b2498 + 3d6226c commit 2690e973161bdce1a26ab20a6c7d7fdd00e343af
@@ -1666,6 +1666,8 @@ BackwardPass::ProcessLoop(BasicBlock * lastBlock)
{
Assert(loop->symsAssignedToInLoop == nullptr);
loop->symsAssignedToInLoop = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
Assert(loop->preservesNumberValue == nullptr);
loop->preservesNumberValue = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
}

FOREACH_BLOCK_BACKWARD_IN_RANGE_DEAD_OR_ALIVE(block, lastBlock, nullptr)
@@ -4486,7 +4488,10 @@ BackwardPass::ProcessNoImplicitCallDef(IR::Instr *const instr)
const bool transferArrayLengthSymUse = !!currentBlock->noImplicitCallArrayLengthSymUses->TestAndClear(dstSym->m_id);

IR::Opnd *const src = instr->GetSrc1();
if(!src || instr->GetSrc2())

// Stop attempting to transfer noImplicitCallUses symbol if the instr is not a transfer instr (based on the opcode's
// flags) or does not have the attributes to be a transfer instr (based on the existance of src and src2).
if(!src || (instr->GetSrc2() && !OpCodeAttr::NonIntTransfer(instr->m_opcode)))
{
return;
}
@@ -5176,16 +5181,24 @@ BackwardPass::UpdateArrayBailOutKind(IR::Instr *const instr)
return;
}

instr->GetDst()->AsIndirOpnd()->AllowConversion(true);
IR::BailOutKind includeBailOutKinds = IR::BailOutInvalid;
if (!baseValueType.IsNotNativeArray() &&
(!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) &&
!currentBlock->noImplicitCallNativeArrayUses->IsEmpty() &&
!(instr->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall))
{
// There is an upwards-exposed use of a native array. Since the array referenced by this instruction can be aliased,
// this instruction needs to bail out if it converts the native array even if this array specifically is not
// upwards-exposed.
includeBailOutKinds |= IR::BailOutConvertedNativeArray;
if (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar())
{
includeBailOutKinds |= IR::BailOutConvertedNativeArray;
}
else
{
// We are assuming that array conversion is impossible here, so make sure we execute code that fails if conversion does happen.
instr->GetDst()->AsIndirOpnd()->AllowConversion(false);
}
}

if(baseOpnd->IsArrayRegOpnd() && baseOpnd->AsArrayRegOpnd()->EliminatedUpperBoundCheck())
@@ -7593,6 +7606,52 @@ BackwardPass::TrackFloatSymEquivalence(IR::Instr *const instr)
}
}

bool
BackwardPass::SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd)
{
Assert(sym->IsStackSym());
if (!opnd->IsRegOpnd())
{
return false;
}
StackSym *opndSym = opnd->AsRegOpnd()->m_sym;

if (sym == opndSym)
{
return true;
}

if (!opndSym->IsSingleDef())
{
return false;
}

if (opndSym->GetInstrDef()->m_opcode == Js::OpCode::LdC_A_I4)
{
return true;
}

return false;
}

bool
BackwardPass::InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym)
{
if (instr->m_opcode == Js::OpCode::Ld_A)
{
if (instr->GetSrc1()->IsRegOpnd())
{
IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd();
if (src1->m_sym->IsSingleDef())
{
instr = src1->m_sym->GetInstrDef();
}
}
}
return (OpCodeAttr::ProducesNumber(instr->m_opcode) ||
(instr->m_opcode == Js::OpCode::Add_A && this->SymIsIntconstOrSelf(defSym, instr->GetSrc1()) && this->SymIsIntconstOrSelf(defSym, instr->GetSrc2())));
}

bool
BackwardPass::ProcessDef(IR::Opnd * opnd)
{
@@ -7607,7 +7666,19 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
this->InvalidateCloneStrCandidate(opnd);
if ((tag == Js::BackwardPhase) && IsPrePass())
{
this->currentPrePassLoop->symsAssignedToInLoop->Set(sym->m_id);
bool firstDef = !this->currentPrePassLoop->symsAssignedToInLoop->TestAndSet(sym->m_id);

if (firstDef)
{
if (this->InstrPreservesNumberValues(this->currentInstr, sym))
{
this->currentPrePassLoop->preservesNumberValue->Set(sym->m_id);
}
}
else if (!this->InstrPreservesNumberValues(this->currentInstr, sym))
{
this->currentPrePassLoop->preservesNumberValue->Clear(sym->m_id);
}
}
}
}
@@ -36,6 +36,9 @@ class BackwardPass
bool ProcessDef(IR::Opnd * opnd);
void ProcessTransfers(IR::Instr * instr);
void ProcessFieldKills(IR::Instr * instr);
bool SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd);
bool InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym);

template<typename T> void ClearBucketsOnFieldKill(IR::Instr *instr, HashTable<T> *table);
StackSym* ProcessByteCodeUsesDst(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
const BVSparse<JitArenaAllocator>* ProcessByteCodeUsesSrcs(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
@@ -587,6 +587,7 @@ class Loop
// cleanup in PreOptPeep in the pre-pass of a loop. For aggressively transferring
// values in prepass, we need to know if a source sym was ever assigned to in a loop.
BVSparse<JitArenaAllocator> *symsAssignedToInLoop;
BVSparse<JitArenaAllocator> *preservesNumberValue;

BailOutInfo * bailOutInfo;
IR::BailOutInstr * toPrimitiveSideEffectCheck;
@@ -732,6 +733,7 @@ class Loop
symsAssignedToInLoop(nullptr),
needImplicitCallBailoutChecksForJsArrayCheckHoist(false),
inductionVariables(nullptr),
preservesNumberValue(nullptr),
dominatingLoopCountableBlock(nullptr),
loopCount(nullptr),
loopCountBasedBoundBaseSyms(nullptr),
@@ -1254,7 +1254,7 @@ void GlobOpt::InsertValueCompensation(
{
IR::Instr *const newInstr =
IR::Instr::New(
Js::OpCode::Ld_I4,
Js::OpCode::Ld_A,
IR::RegOpnd::New(mergedHeadSegmentLengthSym, mergedHeadSegmentLengthSym->GetType(), func),
IR::RegOpnd::New(predecessorHeadSegmentLengthSym, predecessorHeadSegmentLengthSym->GetType(), func),
func);
@@ -2753,10 +2753,43 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
bool
GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const
{
return opnd && (
opnd->m_sym->m_isNotNumber ||
(inGlobOpt && !opnd->GetValueType().IsNumber() && !currentBlock->globOptData.IsTypeSpecialized(opnd->m_sym))
);
if (opnd == nullptr)
{
return false;
}

if (opnd->m_sym->m_isNotNumber)
{
return true;
}

if (!inGlobOpt)
{
return false;
}

if (opnd->GetValueType().IsNumber() || currentBlock->globOptData.IsTypeSpecialized(opnd->m_sym))
{
if (!this->IsLoopPrePass())
{
return false;
}

Value * opndValue = this->currentBlock->globOptData.FindValue(opnd->m_sym);
ValueInfo * opndValueInfo = opndValue ? opndValue->GetValueInfo() : nullptr;
if (!opndValueInfo)
{
return true;
}
if (this->prePassLoop->preservesNumberValue->Test(opnd->m_sym->m_id))
{
return false;
}

return !this->IsSafeToTransferInPrepass(opnd->m_sym, opndValueInfo);
}

return true;
}

bool
@@ -13064,6 +13097,26 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr)
it.RemoveCurrent();
}
}
else if(kills.KillsObjectArraysWithNoMissingValues())
{
// Some operations may kill objects with arrays-with-no-missing-values in unlikely circumstances. Convert their value types to likely
// versions so that the checks have to be redone.
for(auto it = valuesToKillOnCalls->GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext())
{
Value *const value = it.CurrentValue();
ValueInfo *const valueInfo = value->GetValueInfo();
Assert(
valueInfo->IsArrayOrObjectWithArray() ||
valueInfo->IsOptimizedVirtualTypedArray() ||
valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym());
if(!valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsArray() || !valueInfo->HasNoMissingValues())
{
continue;
}
ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false);
it.RemoveCurrent();
}
}

if(kills.KillsNativeArrays())
{
@@ -13684,6 +13737,11 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
{
kills.SetKillsArrayLengths();
}

if(doArrayMissingValueCheckHoist && !(useValueTypes && arrayValueType.IsArray()))
{
kills.SetKillsObjectArraysWithNoMissingValues();
}
break;
}

@@ -313,6 +313,7 @@ class JsArrayKills
{
bool killsAllArrays : 1;
bool killsArraysWithNoMissingValues : 1;
bool killsObjectArraysWithNoMissingValues : 1;
bool killsNativeArrays : 1;
bool killsArrayHeadSegments : 1;
bool killsArrayHeadSegmentLengths : 1;
@@ -338,6 +339,9 @@ class JsArrayKills
bool KillsArraysWithNoMissingValues() const { return killsArraysWithNoMissingValues; }
void SetKillsArraysWithNoMissingValues() { killsArraysWithNoMissingValues = true; }

bool KillsObjectArraysWithNoMissingValues() const { return killsObjectArraysWithNoMissingValues; }
void SetKillsObjectArraysWithNoMissingValues() { killsObjectArraysWithNoMissingValues = true; }

bool KillsNativeArrays() const { return killsNativeArrays; }
void SetKillsNativeArrays() { killsNativeArrays = true; }

@@ -769,6 +773,7 @@ class GlobOpt
void HoistInvariantValueInfo(ValueInfo *const invariantValueInfoToHoist, Value *const valueToUpdate, BasicBlock *const targetBlock);
void OptHoistUpdateValueType(Loop* loop, IR::Instr* instr, IR::Opnd** srcOpndPtr, Value *const srcVal);
bool IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const;

public:
static bool IsTypeSpecPhaseOff(Func const * func);
static bool DoAggressiveIntTypeSpec(Func const * func);
@@ -897,7 +902,7 @@ class GlobOpt
void KillLiveFields(StackSym * stackSym, BVSparse<JitArenaAllocator> * bv);
void KillLiveFields(PropertySym * propertySym, BVSparse<JitArenaAllocator> * bv);
void KillLiveFields(BVSparse<JitArenaAllocator> *const fieldsToKill, BVSparse<JitArenaAllocator> *const bv) const;
void KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func);
void KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func);
void KillAllFields(BVSparse<JitArenaAllocator> * bv);
void SetAnyPropertyMayBeWrittenTo();
void AddToPropertiesWrittenTo(Js::PropertyId propertyId);
@@ -208,7 +208,7 @@ void GlobOpt::KillLiveFields(BVSparse<JitArenaAllocator> *const fieldsToKill, BV
}

void
GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt, Func *func)
{
IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
// obj.x = 10;
@@ -240,6 +240,23 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse<JitArenaAllocator> *
// Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout
this->KillAllObjectTypes(bv);
}
else if ((!valueOpnd || valueOpnd->IsVar()) && this->objectTypeSyms != nullptr)
{
// If we wind up converting a native array, block final-type opt at this point, because we could evolve
// to a type with the wrong type ID. Do this by noting that we may have evolved any type and so must
// check it before evolving it further.
IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
Value * baseValue = baseOpnd ? this->currentBlock->globOptData.FindValue(baseOpnd->m_sym) : nullptr;
ValueInfo * baseValueInfo = baseValue ? baseValue->GetValueInfo() : nullptr;
if (!baseValueInfo || !baseValueInfo->IsNotNativeArray())
{
if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr)
{
this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
}
this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
}
}
}
}

@@ -332,7 +349,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::StElemI_A_Strict:
Assert(dstOpnd != nullptr);
KillLiveFields(this->lengthEquivBv, bv);
KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
@@ -342,7 +359,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::InitComputedProperty:
case Js::OpCode::InitGetElemI:
case Js::OpCode::InitSetElemI:
KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
@@ -352,7 +369,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
case Js::OpCode::DeleteElemI_A:
case Js::OpCode::DeleteElemIStrict_A:
Assert(dstOpnd != nullptr);
KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), nullptr, bv, inGlobOpt, instr->m_func);
break;

case Js::OpCode::DeleteFld:
@@ -3428,7 +3428,14 @@ bool Instr::TransfersSrcValue()

// Consider: Add opcode attribute to indicate whether the opcode would use the value or not

return this->GetDst() != nullptr && this->GetSrc2() == nullptr && !OpCodeAttr::DoNotTransfer(this->m_opcode) && !this->CallsAccessor();
return
this->GetDst() != nullptr &&

// The lack of a Src2 does not always indicate that the instr is not a transfer instr (ex: StSlotChkUndecl).
(this->GetSrc2() == nullptr || OpCodeAttr::NonIntTransfer(this->m_opcode)) &&

!OpCodeAttr::DoNotTransfer(this->m_opcode) &&
!this->CallsAccessor();
}


0 comments on commit 2690e97

Please sign in to comment.
You can’t perform that action at this time.