Skip to content

Commit c70af48

Browse files
pleathwyrichte
authored andcommitted
[CVE-2019-1195] Chakra Type Confusion RCE
1 parent ae8a8d9 commit c70af48

File tree

5 files changed

+55
-33
lines changed

5 files changed

+55
-33
lines changed

lib/Backend/BackwardPass.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
BackwardPass::BackwardPass(Func * func, GlobOpt * globOpt, Js::Phase tag)
1111
: func(func), globOpt(globOpt), tag(tag), currentPrePassLoop(nullptr), tempAlloc(nullptr),
1212
preOpBailOutInstrToProcess(nullptr),
13-
considerSymAsRealUseInNoImplicitCallUses(nullptr),
1413
isCollectionPass(false), currentRegion(nullptr),
1514
collectionPassSubPhase(CollectionPassSubPhase::None),
1615
isLoopPrepass(false)
@@ -412,6 +411,8 @@ BackwardPass::Optimize()
412411
candidateSymsRequiredToBeInt = &localCandidateSymsRequiredToBeInt;
413412
BVSparse<JitArenaAllocator> localCandidateSymsRequiredToBeLossyInt(tempAlloc);
414413
candidateSymsRequiredToBeLossyInt = &localCandidateSymsRequiredToBeLossyInt;
414+
BVSparse<JitArenaAllocator> localConsiderSymsAsRealUsesInNoImplicitCallUses(tempAlloc);
415+
considerSymsAsRealUsesInNoImplicitCallUses = &localConsiderSymsAsRealUsesInNoImplicitCallUses;
415416
intOverflowCurrentlyMattersInRange = true;
416417

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

3758-
Assert(!considerSymAsRealUseInNoImplicitCallUses);
3759+
Assert(considerSymsAsRealUsesInNoImplicitCallUses->IsEmpty());
37593760

37603761
#if DBG_DUMP
37613762
TraceBlockUses(block, false);
@@ -4228,9 +4229,8 @@ BackwardPass::ProcessNoImplicitCallUses(IR::Instr *const instr)
42284229
{
42294230
IR::RegOpnd *const regSrc = src->AsRegOpnd();
42304231
sym = regSrc->m_sym;
4231-
if(considerSymAsRealUseInNoImplicitCallUses && considerSymAsRealUseInNoImplicitCallUses == sym)
4232+
if(considerSymsAsRealUsesInNoImplicitCallUses->TestAndClear(sym->m_id))
42324233
{
4233-
considerSymAsRealUseInNoImplicitCallUses = nullptr;
42344234
ProcessStackSymUse(sym->AsStackSym(), true);
42354235
}
42364236
if(regSrc->IsArrayRegOpnd())
@@ -4648,7 +4648,6 @@ BackwardPass::ProcessArrayRegOpndUse(IR::Instr *const instr, IR::ArrayRegOpnd *c
46484648
// ProcessNoImplicitCallUses automatically marks JS array reg opnds and their corresponding syms as live. A typed
46494649
// array's head segment length sym also needs to be marked as live at its use in the NoImplicitCallUses instruction,
46504650
// but it is just in a reg opnd. Flag the opnd to have the sym be marked as live when that instruction is processed.
4651-
Assert(!considerSymAsRealUseInNoImplicitCallUses);
46524651
IR::Opnd *const use =
46534652
FindNoImplicitCallUse(
46544653
instr,
@@ -4659,7 +4658,7 @@ BackwardPass::ProcessArrayRegOpndUse(IR::Instr *const instr, IR::ArrayRegOpnd *c
46594658
});
46604659
if(use)
46614660
{
4662-
considerSymAsRealUseInNoImplicitCallUses = arrayRegOpnd->HeadSegmentLengthSym();
4661+
considerSymsAsRealUsesInNoImplicitCallUses->Set(arrayRegOpnd->HeadSegmentLengthSym()->m_id);
46634662
}
46644663
}
46654664
}

lib/Backend/BackwardPass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class BackwardPass
191191
BVSparse<JitArenaAllocator> * intOverflowDoesNotMatterInRangeBySymId;
192192
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeInt;
193193
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeLossyInt;
194-
StackSym * considerSymAsRealUseInNoImplicitCallUses;
194+
BVSparse<JitArenaAllocator> * considerSymsAsRealUsesInNoImplicitCallUses;
195195
bool intOverflowCurrentlyMattersInRange;
196196
bool isCollectionPass;
197197
enum class CollectionPassSubPhase

lib/Backend/GlobOpt.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13149,6 +13149,37 @@ GlobOpt::OptArraySrc(IR::Instr ** const instrRef, Value ** src1Val, Value ** src
1314913149
arraySrcOpt.Optimize();
1315013150
}
1315113151

13152+
void
13153+
GlobOpt::ProcessNoImplicitCallArrayUses(IR::RegOpnd * baseOpnd, IR::ArrayRegOpnd * baseArrayOpnd, IR::Instr * instr, bool isLikelyJsArray, bool useNoMissingValues)
13154+
{
13155+
if (isLikelyJsArray)
13156+
{
13157+
// Insert an instruction to indicate to the dead-store pass that implicit calls need to be kept disabled until this
13158+
// instruction. Operations other than LdElem, StElem and IsIn don't benefit much from arrays having no missing values,
13159+
// so no need to ensure that the array still has no missing values. For a particular array, if none of the accesses
13160+
// benefit much from the no-missing-values information, it may be beneficial to avoid checking for no missing
13161+
// values, especially in the case for a single array access, where the cost of the check could be relatively
13162+
// significant. An StElem has to do additional checks in the common path if the array may have missing values, and
13163+
// a StElem that operates on an array that has no missing values is more likely to keep the no-missing-values info
13164+
// on the array more precise, so it still benefits a little from the no-missing-values info.
13165+
this->CaptureNoImplicitCallUses(baseOpnd, isLikelyJsArray);
13166+
}
13167+
else if (baseArrayOpnd && baseArrayOpnd->HeadSegmentLengthSym())
13168+
{
13169+
// A typed array's array buffer may be transferred to a web worker as part of an implicit call, in which case the typed
13170+
// array's length is set to zero. Insert an instruction to indicate to the dead-store pass that implicit calls need to
13171+
// be disabled until this instruction.
13172+
IR::RegOpnd *const headSegmentLengthOpnd =
13173+
IR::RegOpnd::New(
13174+
baseArrayOpnd->HeadSegmentLengthSym(),
13175+
baseArrayOpnd->HeadSegmentLengthSym()->GetType(),
13176+
instr->m_func);
13177+
13178+
const IR::AutoReuseOpnd autoReuseHeadSegmentLengthOpnd(headSegmentLengthOpnd, instr->m_func);
13179+
this->CaptureNoImplicitCallUses(headSegmentLengthOpnd, false);
13180+
}
13181+
}
13182+
1315213183
void
1315313184
GlobOpt::CaptureNoImplicitCallUses(
1315413185
IR::Opnd *opnd,
@@ -17038,11 +17069,27 @@ GlobOpt::EmitMemop(Loop * loop, LoopCount *loopCount, const MemOpEmitData* emitD
1703817069
}
1703917070
#endif
1704017071

17072+
Assert(noImplicitCallUsesToInsert->Count() == 0);
17073+
bool isLikelyJsArray;
17074+
if (emitData->stElemInstr->GetDst()->IsIndirOpnd())
17075+
{
17076+
baseOpnd = emitData->stElemInstr->GetDst()->AsIndirOpnd()->GetBaseOpnd();
17077+
isLikelyJsArray = baseOpnd->GetValueType().IsLikelyArrayOrObjectWithArray();
17078+
ProcessNoImplicitCallArrayUses(baseOpnd, baseOpnd->IsArrayRegOpnd() ? baseOpnd->AsArrayRegOpnd() : nullptr, emitData->stElemInstr, isLikelyJsArray, true);
17079+
}
1704117080
RemoveMemOpSrcInstr(memopInstr, emitData->stElemInstr, emitData->block);
1704217081
if (!isMemset)
1704317082
{
17083+
if (((MemCopyEmitData*)emitData)->ldElemInstr->GetSrc1()->IsIndirOpnd())
17084+
{
17085+
baseOpnd = ((MemCopyEmitData*)emitData)->ldElemInstr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd();
17086+
isLikelyJsArray = baseOpnd->GetValueType().IsLikelyArrayOrObjectWithArray();
17087+
ProcessNoImplicitCallArrayUses(baseOpnd, baseOpnd->IsArrayRegOpnd() ? baseOpnd->AsArrayRegOpnd() : nullptr, emitData->stElemInstr, isLikelyJsArray, true);
17088+
}
1704417089
RemoveMemOpSrcInstr(memopInstr, ((MemCopyEmitData*)emitData)->ldElemInstr, emitData->block);
1704517090
}
17091+
InsertNoImplicitCallUses(memopInstr);
17092+
noImplicitCallUsesToInsert->Clear();
1704617093
}
1704717094

1704817095
bool

lib/Backend/GlobOpt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ class GlobOpt
721721
private:
722722
void CaptureNoImplicitCallUses(IR::Opnd *opnd, const bool usesNoMissingValuesInfo, IR::Instr *const includeCurrentInstr = nullptr);
723723
void InsertNoImplicitCallUses(IR::Instr *const instr);
724+
void ProcessNoImplicitCallArrayUses(IR::RegOpnd * baseOpnd, IR::ArrayRegOpnd * baseArrayOpnd, IR::Instr * instr, bool isLikelyJsArray, bool useNoMissingValues);
724725
void PrepareLoopArrayCheckHoist();
725726

726727
public:

lib/Backend/GlobOptArrays.cpp

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,32 +1929,7 @@ void GlobOpt::ArraySrcOpt::Optimize()
19291929
baseArrayOpnd = nullptr;
19301930
}
19311931

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

19591934
const auto OnEliminated = [&](const Js::Phase phase, const char *const eliminatedLoad)
19601935
{

0 commit comments

Comments
 (0)