Skip to content

Commit

Permalink
[MERGE #6243 @wyrichte] ChakraCore servicing update for August, 2019
Browse files Browse the repository at this point in the history
Merge pull request #6243 from wyrichte:gh_servicing_1908

This release addresses the following issues:
CVE-2019-1197
CVE-2019-1141
CVE-2019-1196
CVE-2019-1139
CVE-2019-1131
CVE-2019-1195
  • Loading branch information
wyrichte committed Aug 13, 2019
2 parents 75162b7 + 450a349 commit 6b1250b
Show file tree
Hide file tree
Showing 33 changed files with 888 additions and 771 deletions.
2 changes: 1 addition & 1 deletion Build/NuGet/.pack-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.11.11
1.11.12
12 changes: 6 additions & 6 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
BackwardPass::BackwardPass(Func * func, GlobOpt * globOpt, Js::Phase tag)
: func(func), globOpt(globOpt), tag(tag), currentPrePassLoop(nullptr), tempAlloc(nullptr),
preOpBailOutInstrToProcess(nullptr),
considerSymAsRealUseInNoImplicitCallUses(nullptr),
isCollectionPass(false), currentRegion(nullptr),
collectionPassSubPhase(CollectionPassSubPhase::None),
isLoopPrepass(false)
Expand Down Expand Up @@ -412,6 +411,8 @@ BackwardPass::Optimize()
candidateSymsRequiredToBeInt = &localCandidateSymsRequiredToBeInt;
BVSparse<JitArenaAllocator> localCandidateSymsRequiredToBeLossyInt(tempAlloc);
candidateSymsRequiredToBeLossyInt = &localCandidateSymsRequiredToBeLossyInt;
BVSparse<JitArenaAllocator> localConsiderSymsAsRealUsesInNoImplicitCallUses(tempAlloc);
considerSymsAsRealUsesInNoImplicitCallUses = &localConsiderSymsAsRealUsesInNoImplicitCallUses;
intOverflowCurrentlyMattersInRange = true;

FloatSymEquivalenceMap localFloatSymEquivalenceMap(tempAlloc);
Expand Down Expand Up @@ -3755,7 +3756,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
block->loop->regAlloc.liveOnBackEdgeSyms = block->upwardExposedUses->CopyNew(this->func->m_alloc);
}

Assert(!considerSymAsRealUseInNoImplicitCallUses);
Assert(considerSymsAsRealUsesInNoImplicitCallUses->IsEmpty());

#if DBG_DUMP
TraceBlockUses(block, false);
Expand Down Expand Up @@ -4228,9 +4229,8 @@ BackwardPass::ProcessNoImplicitCallUses(IR::Instr *const instr)
{
IR::RegOpnd *const regSrc = src->AsRegOpnd();
sym = regSrc->m_sym;
if(considerSymAsRealUseInNoImplicitCallUses && considerSymAsRealUseInNoImplicitCallUses == sym)
if(considerSymsAsRealUsesInNoImplicitCallUses->TestAndClear(sym->m_id))
{
considerSymAsRealUseInNoImplicitCallUses = nullptr;
ProcessStackSymUse(sym->AsStackSym(), true);
}
if(regSrc->IsArrayRegOpnd())
Expand Down Expand Up @@ -4648,7 +4648,6 @@ BackwardPass::ProcessArrayRegOpndUse(IR::Instr *const instr, IR::ArrayRegOpnd *c
// ProcessNoImplicitCallUses automatically marks JS array reg opnds and their corresponding syms as live. A typed
// array's head segment length sym also needs to be marked as live at its use in the NoImplicitCallUses instruction,
// but it is just in a reg opnd. Flag the opnd to have the sym be marked as live when that instruction is processed.
Assert(!considerSymAsRealUseInNoImplicitCallUses);
IR::Opnd *const use =
FindNoImplicitCallUse(
instr,
Expand All @@ -4659,7 +4658,7 @@ BackwardPass::ProcessArrayRegOpndUse(IR::Instr *const instr, IR::ArrayRegOpnd *c
});
if(use)
{
considerSymAsRealUseInNoImplicitCallUses = arrayRegOpnd->HeadSegmentLengthSym();
considerSymsAsRealUsesInNoImplicitCallUses->Set(arrayRegOpnd->HeadSegmentLengthSym()->m_id);
}
}
}
Expand Down Expand Up @@ -6555,6 +6554,7 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
case Js::OpCode::Coerce_Regex:
case Js::OpCode::Coerce_StrOrRegex:
case Js::OpCode::Conv_PrimStr:
case Js::OpCode::Conv_Prop:
// These instructions don't generate -0, and their behavior is the same for any src that is -0 or +0
SetNegativeZeroDoesNotMatterIfLastUse(instr->GetSrc1());
SetNegativeZeroDoesNotMatterIfLastUse(instr->GetSrc2());
Expand Down
2 changes: 1 addition & 1 deletion lib/Backend/BackwardPass.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class BackwardPass
BVSparse<JitArenaAllocator> * intOverflowDoesNotMatterInRangeBySymId;
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeInt;
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeLossyInt;
StackSym * considerSymAsRealUseInNoImplicitCallUses;
BVSparse<JitArenaAllocator> * considerSymsAsRealUsesInNoImplicitCallUses;
bool intOverflowCurrentlyMattersInRange;
bool isCollectionPass;
enum class CollectionPassSubPhase
Expand Down
58 changes: 56 additions & 2 deletions lib/Backend/GlobOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2695,7 +2695,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
}

bool
GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const
GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt, bool *isSafeToTransferInPrepass /*=nullptr*/) const
{
if (opnd == nullptr)
{
Expand Down Expand Up @@ -2725,12 +2725,18 @@ GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const
{
return true;
}

bool isSafeToTransfer = this->IsSafeToTransferInPrepass(opnd->m_sym, opndValueInfo);
if (isSafeToTransferInPrepass != nullptr)
{
*isSafeToTransferInPrepass = isSafeToTransfer;
}
if (this->prePassLoop->preservesNumberValue->Test(opnd->m_sym->m_id))
{
return false;
}

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

return true;
Expand Down Expand Up @@ -13143,6 +13149,37 @@ GlobOpt::OptArraySrc(IR::Instr ** const instrRef, Value ** src1Val, Value ** src
arraySrcOpt.Optimize();
}

void
GlobOpt::ProcessNoImplicitCallArrayUses(IR::RegOpnd * baseOpnd, IR::ArrayRegOpnd * baseArrayOpnd, IR::Instr * instr, bool isLikelyJsArray, bool useNoMissingValues)
{
if (isLikelyJsArray)
{
// Insert an instruction to indicate to the dead-store pass that implicit calls need to be kept disabled until this
// instruction. Operations other than LdElem, StElem and IsIn don't benefit much from arrays having no missing values,
// so no need to ensure that the array still has no missing values. For a particular array, if none of the accesses
// benefit much from the no-missing-values information, it may be beneficial to avoid checking for no missing
// values, especially in the case for a single array access, where the cost of the check could be relatively
// significant. An StElem has to do additional checks in the common path if the array may have missing values, and
// a StElem that operates on an array that has no missing values is more likely to keep the no-missing-values info
// on the array more precise, so it still benefits a little from the no-missing-values info.
this->CaptureNoImplicitCallUses(baseOpnd, isLikelyJsArray);
}
else if (baseArrayOpnd && baseArrayOpnd->HeadSegmentLengthSym())
{
// A typed array's array buffer may be transferred to a web worker as part of an implicit call, in which case the typed
// array's length is set to zero. Insert an instruction to indicate to the dead-store pass that implicit calls need to
// be disabled until this instruction.
IR::RegOpnd *const headSegmentLengthOpnd =
IR::RegOpnd::New(
baseArrayOpnd->HeadSegmentLengthSym(),
baseArrayOpnd->HeadSegmentLengthSym()->GetType(),
instr->m_func);

const IR::AutoReuseOpnd autoReuseHeadSegmentLengthOpnd(headSegmentLengthOpnd, instr->m_func);
this->CaptureNoImplicitCallUses(headSegmentLengthOpnd, false);
}
}

void
GlobOpt::CaptureNoImplicitCallUses(
IR::Opnd *opnd,
Expand Down Expand Up @@ -13464,6 +13501,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
case IR::HelperArray_Splice:
case IR::HelperArray_Unshift:
case IR::HelperArray_Concat:
case IR::HelperArray_Slice:
kills.SetKillsArrayHeadSegments();
kills.SetKillsArrayHeadSegmentLengths();
break;
Expand Down Expand Up @@ -17031,11 +17069,27 @@ GlobOpt::EmitMemop(Loop * loop, LoopCount *loopCount, const MemOpEmitData* emitD
}
#endif

Assert(noImplicitCallUsesToInsert->Count() == 0);
bool isLikelyJsArray;
if (emitData->stElemInstr->GetDst()->IsIndirOpnd())
{
baseOpnd = emitData->stElemInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd();
isLikelyJsArray = baseOpnd->GetValueType().IsLikelyArrayOrObjectWithArray();
ProcessNoImplicitCallArrayUses(baseOpnd, baseOpnd->IsArrayRegOpnd() ? baseOpnd->AsArrayRegOpnd() : nullptr, emitData->stElemInstr, isLikelyJsArray, true);
}
RemoveMemOpSrcInstr(memopInstr, emitData->stElemInstr, emitData->block);
if (!isMemset)
{
if (((MemCopyEmitData*)emitData)->ldElemInstr->GetSrc1()->IsIndirOpnd())
{
baseOpnd = ((MemCopyEmitData*)emitData)->ldElemInstr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd();
isLikelyJsArray = baseOpnd->GetValueType().IsLikelyArrayOrObjectWithArray();
ProcessNoImplicitCallArrayUses(baseOpnd, baseOpnd->IsArrayRegOpnd() ? baseOpnd->AsArrayRegOpnd() : nullptr, emitData->stElemInstr, isLikelyJsArray, true);
}
RemoveMemOpSrcInstr(memopInstr, ((MemCopyEmitData*)emitData)->ldElemInstr, emitData->block);
}
InsertNoImplicitCallUses(memopInstr);
noImplicitCallUsesToInsert->Clear();
}

bool
Expand Down
3 changes: 2 additions & 1 deletion lib/Backend/GlobOpt.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ class GlobOpt
private:
void CaptureNoImplicitCallUses(IR::Opnd *opnd, const bool usesNoMissingValuesInfo, IR::Instr *const includeCurrentInstr = nullptr);
void InsertNoImplicitCallUses(IR::Instr *const instr);
void ProcessNoImplicitCallArrayUses(IR::RegOpnd * baseOpnd, IR::ArrayRegOpnd * baseArrayOpnd, IR::Instr * instr, bool isLikelyJsArray, bool useNoMissingValues);
void PrepareLoopArrayCheckHoist();

public:
Expand Down Expand Up @@ -773,7 +774,7 @@ class GlobOpt
const bool lossy = false, const bool forceInvariantHoisting = false, IR::BailOutKind bailoutKind = IR::BailOutInvalid);
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;
bool IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt, bool *isSafeToTransferInPrepass = nullptr) const;

public:
static bool IsTypeSpecPhaseOff(Func const * func);
Expand Down
27 changes: 1 addition & 26 deletions lib/Backend/GlobOptArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1929,32 +1929,7 @@ void GlobOpt::ArraySrcOpt::Optimize()
baseArrayOpnd = nullptr;
}

if (isLikelyJsArray)
{
// Insert an instruction to indicate to the dead-store pass that implicit calls need to be kept disabled until this
// instruction. Operations other than LdElem, StElem and IsIn don't benefit much from arrays having no missing values,
// so no need to ensure that the array still has no missing values. For a particular array, if none of the accesses
// benefit much from the no-missing-values information, it may be beneficial to avoid checking for no missing
// values, especially in the case for a single array access, where the cost of the check could be relatively
// significant. An StElem has to do additional checks in the common path if the array may have missing values, and
// a StElem that operates on an array that has no missing values is more likely to keep the no-missing-values info
// on the array more precise, so it still benefits a little from the no-missing-values info.
globOpt->CaptureNoImplicitCallUses(baseOpnd, isLoad || isStore || instr->m_opcode == Js::OpCode::IsIn);
}
else if (baseArrayOpnd && baseArrayOpnd->HeadSegmentLengthSym())
{
// A typed array's array buffer may be transferred to a web worker as part of an implicit call, in which case the typed
// array's length is set to zero. Insert an instruction to indicate to the dead-store pass that implicit calls need to
// be disabled until this instruction.
IR::RegOpnd *const headSegmentLengthOpnd =
IR::RegOpnd::New(
baseArrayOpnd->HeadSegmentLengthSym(),
baseArrayOpnd->HeadSegmentLengthSym()->GetType(),
instr->m_func);

const IR::AutoReuseOpnd autoReuseHeadSegmentLengthOpnd(headSegmentLengthOpnd, instr->m_func);
globOpt->CaptureNoImplicitCallUses(headSegmentLengthOpnd, false);
}
globOpt->ProcessNoImplicitCallArrayUses(baseOpnd, baseArrayOpnd, instr, isLikelyJsArray, isLoad || isStore || instr->m_opcode == Js::OpCode::IsIn);

const auto OnEliminated = [&](const Js::Phase phase, const char *const eliminatedLoad)
{
Expand Down
17 changes: 15 additions & 2 deletions lib/Backend/GlobOptFields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse
// - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that
// if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next
// pass based on the value.
if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indexOpnd, inGlobOpt))
bool isSafeToTransfer = true;
if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indexOpnd, inGlobOpt, &isSafeToTransfer))
{
this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms
SetAnyPropertyMayBeWrittenTo();
Expand All @@ -236,7 +237,7 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse
ValueInfo * indexValueInfo = indexValue ? indexValue->GetValueInfo() : nullptr;
int indexLowerBound = 0;

if (indirOpnd->GetOffset() < 0 || (indexOpnd && (!indexValueInfo || !indexValueInfo->TryGetIntConstantLowerBound(&indexLowerBound, false) || indexLowerBound < 0)))
if (!isSafeToTransfer || indirOpnd->GetOffset() < 0 || (indexOpnd && (!indexValueInfo || !indexValueInfo->TryGetIntConstantLowerBound(&indexLowerBound, false) || indexLowerBound < 0)))
{
// 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);
Expand Down Expand Up @@ -517,6 +518,18 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
}
break;

case IR::JnHelperMethod::HelperArray_Slice:
case IR::JnHelperMethod::HelperArray_Concat:
if (inGlobOpt && this->objectTypeSyms)
{
if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr)
{
this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
}
this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
}
break;

case IR::JnHelperMethod::HelperRegExp_Exec:
case IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed:
case IR::JnHelperMethod::HelperRegExp_ExecResultUsed:
Expand Down
15 changes: 11 additions & 4 deletions lib/Backend/GlobOptIntBounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1278,13 +1278,20 @@ GlobOpt::InvalidateInductionVariables(IR::Instr * instr)
}

// If this is an induction variable, then treat it the way the prepass would have if it had seen
// the assignment and the resulting change to the value number, and mark it as indeterminate.
// the assignment and the resulting change to the value number, and mark induction variables
// for the loop as indeterminate.
// We need to invalidate all induction variables for the loop, because we might have used the
// invalidated induction variable to calculate the loopCount, and this now invalid loopCount
// also impacts bound checks for secondary induction variables
for (Loop * loop = this->currentBlock->loop; loop; loop = loop->parent)
{
InductionVariable *iv = nullptr;
if (loop->inductionVariables && loop->inductionVariables->TryGetReference(dstSym->m_id, &iv))
if (loop->inductionVariables && loop->inductionVariables->ContainsKey(dstSym->m_id))
{
iv->SetChangeIsIndeterminate();
for (auto it = loop->inductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable& inductionVariable = it.CurrentValueReference();
inductionVariable.SetChangeIsIndeterminate();
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/Backend/JnHelperMethodList.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ HELPERCALL_MATH(Op_MaxInAnArray, Js::JavascriptMath::MaxInAnArray, AttrCanThrow)
HELPERCALL_MATH(Op_MinInAnArray, Js::JavascriptMath::MinInAnArray, AttrCanThrow)

HELPERCALLCHK(Op_ConvString, Js::JavascriptConversion::ToString, AttrCanThrow)
HELPERCALLCHK(Op_ConvPropertyKey, Js::JavascriptOperators::OP_ToPropertyKey, AttrCanThrow)
HELPERCALLCHK(Op_CoerseString, Js::JavascriptConversion::CoerseString, AttrCanThrow)
HELPERCALLCHK(Op_CoerseRegex, (Js::JavascriptRegExp* (*) (Js::Var aValue, Js::Var options, Js::ScriptContext *scriptContext))Js::JavascriptRegExp::CreateRegEx, AttrCanThrow)

Expand Down
10 changes: 10 additions & 0 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,10 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
this->LowerConvPrimStr(instr);
break;

case Js::OpCode::Conv_Prop:
this->LowerConvPropertyKey(instr);
break;

case Js::OpCode::ClearAttributes:
this->LowerBinaryHelper(instr, IR::HelperOP_ClearAttributes);
break;
Expand Down Expand Up @@ -25474,6 +25478,12 @@ Lowerer::GenerateGetImmutableOrScriptUnreferencedString(IR::RegOpnd * strOpnd, I
return dstOpnd;
}

void
Lowerer::LowerConvPropertyKey(IR::Instr* instr)
{
LowerConvStrCommon(IR::HelperOp_ConvPropertyKey, instr);
}

void
Lowerer::LowerConvStrCommon(IR::JnHelperMethod helper, IR::Instr * instr)
{
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/Lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,8 @@ class Lowerer
void LowerConvPrimStr(IR::Instr * instr);
void LowerConvStrCommon(IR::JnHelperMethod helper, IR::Instr * instr);

void LowerConvPropertyKey(IR::Instr* instr);

void GenerateRecyclerAlloc(IR::JnHelperMethod allocHelper, size_t allocSize, IR::RegOpnd* newObjDst, IR::Instr* insertionPointInstr, bool inOpHelper = false);

template <typename ArrayType>
Expand Down
2 changes: 1 addition & 1 deletion lib/Common/ChakraCoreVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// ChakraCore version number definitions (used in ChakraCore binary metadata)
#define CHAKRA_CORE_MAJOR_VERSION 1
#define CHAKRA_CORE_MINOR_VERSION 11
#define CHAKRA_CORE_PATCH_VERSION 11
#define CHAKRA_CORE_PATCH_VERSION 12
#define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0.

// -------------
Expand Down
Loading

0 comments on commit 6b1250b

Please sign in to comment.