Răsfoiți Sursa

[CVE-2019-1195] Chakra Type Confusion RCE

Paul Leathers 6 ani în urmă
părinte
comite
c70af488e4

+ 5 - 6
lib/Backend/BackwardPass.cpp

@@ -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)
@@ -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);
@@ -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);
@@ -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())
@@ -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,
@@ -4659,7 +4658,7 @@ BackwardPass::ProcessArrayRegOpndUse(IR::Instr *const instr, IR::ArrayRegOpnd *c
                     });
             if(use)
             {
-                considerSymAsRealUseInNoImplicitCallUses = arrayRegOpnd->HeadSegmentLengthSym();
+                considerSymsAsRealUsesInNoImplicitCallUses->Set(arrayRegOpnd->HeadSegmentLengthSym()->m_id);
             }
         }
     }

+ 1 - 1
lib/Backend/BackwardPass.h

@@ -191,7 +191,7 @@ private:
     BVSparse<JitArenaAllocator> * intOverflowDoesNotMatterInRangeBySymId;
     BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeInt;
     BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeLossyInt;
-    StackSym * considerSymAsRealUseInNoImplicitCallUses;
+    BVSparse<JitArenaAllocator> * considerSymsAsRealUsesInNoImplicitCallUses;
     bool intOverflowCurrentlyMattersInRange;
     bool isCollectionPass;
     enum class CollectionPassSubPhase

+ 47 - 0
lib/Backend/GlobOpt.cpp

@@ -13149,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,
@@ -17038,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

+ 1 - 0
lib/Backend/GlobOpt.h

@@ -721,6 +721,7 @@ private:
 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:

+ 1 - 26
lib/Backend/GlobOptArrays.cpp

@@ -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)
     {