Procházet zdrojové kódy

[MERGE #3166 @jianchun] 17-06 ChakraCore servicing release

Merge pull request #3166 from jianchun:1706

Fixes the following CVEs impacting ChakraCore:

[CVE-2017-0228]
[CVE-2017-8499]
[CVE-2017-8518]
[CVE-2017-8520]
[CVE-2017-8522]
[CVE-2017-8524]
[CVE-2017-8548]
Jianchun Xu před 8 roky
rodič
revize
93ec291cff

+ 5 - 0
Build/Chakra.Build.props

@@ -16,6 +16,7 @@
     <ClCompile>
       <PreprocessorDefinitions>
         %(PreprocessorDefinitions);
+        _CHAKRACOREBUILD;
         _WIN32_WINNT=$(Win32_WinNTVersion);
         WINVER=$(Win32_WinNTVersion);
         WIN32_LEAN_AND_MEAN=1
@@ -52,6 +53,10 @@
       <Optimization Condition="'$(ENABLE_CODECOVERAGE)'=='true'">Disabled</Optimization>
     </ClCompile>
 
+    <ResourceCompile>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);_CHAKRACOREBUILD</PreprocessorDefinitions>
+    </ResourceCompile>
+
     <Link>
       <!-- ======== For Code Coverage ======== -->
       <AdditionalOptions Condition="'$(ENABLE_CODECOVERAGE)'=='true'">%(AdditionalOptions) /DEBUGTYPE:CV,FIXUP</AdditionalOptions>

+ 1 - 1
Build/Common.Build.props

@@ -31,7 +31,7 @@
       <AdditionalOptions>%(AdditionalOptions) -sal_local</AdditionalOptions>
     </Midl>
     <ClCompile>
-      <PreprocessorDefinitions>%(PreprocessorDefinitions);_CHAKRACOREBUILD;NOMINMAX;USE_EDGEMODE_JSRT</PreprocessorDefinitions>
+      <PreprocessorDefinitions>%(PreprocessorDefinitions);NOMINMAX;USE_EDGEMODE_JSRT</PreprocessorDefinitions>
       <!-- Some of our STDMETHOD can throw
            TODO: Code review STDMETHOD and separate out API that can throw and those that can't -->
       <PreprocessorDefinitions>%(PreprocessorDefinitions);COM_STDMETHOD_CAN_THROW</PreprocessorDefinitions>

+ 40 - 8
lib/Backend/Inline.cpp

@@ -4674,16 +4674,22 @@ Inline::MapFormals(Func *inlinee,
             {
                 break;
             }
+
+            int excess;
+            uint restFuncFormalCount = 0;
             if (instr->m_func != inlinee)
             {
-                // this can happen only when we are inlining a function which has inlined an apply call with the arguments object
-                formalCount = instr->m_func->GetJITFunctionBody()->GetInParamsCount();
+                restFuncFormalCount = instr->m_func->GetJITFunctionBody()->GetInParamsCount();
+                Assert(restFuncFormalCount < 1 << 24); // 24 bits for arg count (see CallInfo.h)
+                excess = actualCount - restFuncFormalCount;
+            }
+            else
+            {
+                excess = actualCount - formalCount;
             }
-
             IR::Opnd *restDst = instr->GetDst();
 
             Assert(actualCount < 1 << 24 && formalCount < 1 << 24); // 24 bits for arg count (see CallInfo.h)
-            int excess = actualCount - formalCount;
 
             if (excess < 0)
             {
@@ -4701,11 +4707,37 @@ Inline::MapFormals(Func *inlinee,
             IR::Instr *newArrInstr = IR::Instr::New(Js::OpCode::NewScArray, restDst, IR::IntConstOpnd::New(excess, TyUint32, inlinee), inlinee);
             instr->InsertBefore(newArrInstr);
 
-            for (uint i = formalCount; i < actualCount; ++i)
+            if (instr->m_func != inlinee)
             {
-                IR::IndirOpnd *arrayLocOpnd = IR::IndirOpnd::New(restDst->AsRegOpnd(), i - formalCount, TyVar, inlinee);
-                IR::Instr *stElemInstr = IR::Instr::New(Js::OpCode::StElemC, arrayLocOpnd, argOutsExtra[i]->GetBytecodeArgOutCapture()->GetDst(), inlinee);
-                instr->InsertBefore(stElemInstr);
+                uint index = 0;
+                for (uint i = restFuncFormalCount; i < min(actualCount, formalCount); ++i)
+                {
+                    IR::IndirOpnd *arrayLocOpnd = IR::IndirOpnd::New(restDst->AsRegOpnd(), index, TyVar, inlinee);
+                    IR::Instr *stElemInstr = IR::Instr::New(Js::OpCode::StElemC, arrayLocOpnd, argOuts[i]->GetBytecodeArgOutCapture()->GetDst(), inlinee);
+                    instr->InsertBefore(stElemInstr);
+                    index++;
+                }
+                for (uint i = max(formalCount, restFuncFormalCount); i < actualCount; ++i)
+                {
+                    IR::IndirOpnd *arrayLocOpnd = IR::IndirOpnd::New(restDst->AsRegOpnd(), index, TyVar, inlinee);
+                    IR::Instr *stElemInstr = IR::Instr::New(Js::OpCode::StElemC, arrayLocOpnd, argOutsExtra[i]->GetBytecodeArgOutCapture()->GetDst(), inlinee);
+                    instr->InsertBefore(stElemInstr);
+                    index++;
+                }
+                AssertMsg(index == (uint)excess, "Incorrect rest args built");
+                if (index != (uint)excess)
+                {
+                    throw Js::OperationAbortedException();
+                }
+            }
+            else
+            {
+                for (uint i = formalCount; i < actualCount; ++i)
+                {
+                    IR::IndirOpnd *arrayLocOpnd = IR::IndirOpnd::New(restDst->AsRegOpnd(), i - formalCount, TyVar, inlinee);
+                    IR::Instr *stElemInstr = IR::Instr::New(Js::OpCode::StElemC, arrayLocOpnd, argOutsExtra[i]->GetBytecodeArgOutCapture()->GetDst(), inlinee);
+                    instr->InsertBefore(stElemInstr);
+                }
             }
 
             instr->Remove();

+ 12 - 0
lib/Backend/Lower.cpp

@@ -15889,10 +15889,22 @@ Lowerer::GenerateFastElemIIntIndexCommon(
                 // For typed array, call ToNumber before we fallThrough.
                 if (instr->GetSrc1()->GetType() == TyVar && !instr->GetSrc1()->GetValueType().IsPrimitive())
                 {
+                    // Enter an ophelper block
+                    IR::LabelInstr * opHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
+                    instr->InsertBefore(opHelper);
+
                     IR::Instr *toNumberInstr = IR::Instr::New(Js::OpCode::Call, this->m_func);
                     toNumberInstr->SetSrc1(instr->GetSrc1());
                     instr->InsertBefore(toNumberInstr);
 
+                    if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
+                    {
+                        // Bail out if this conversion triggers implicit calls.
+                        toNumberInstr = toNumberInstr->ConvertToBailOutInstr(instr->GetBailOutInfo(), bailOutKind);
+                        IR::Instr * instrShare = instr->ShareBailOut();
+                        LowerBailTarget(instrShare);
+                    }
+
                     LowerUnaryHelperMem(toNumberInstr, IR::HelperOp_ConvNumber_Full);
                 }
                 InsertBranch(Js::OpCode::Br, labelFallthrough, instr);  //Jump to fallThrough

+ 5 - 1
lib/JITServer/JITServer.cpp

@@ -422,11 +422,15 @@ ServerCleanupScriptContext(
         return RPC_S_INVALID_ARG;
     }
 
+    if (!scriptContextInfo->IsClosed())
+    {
+        scriptContextInfo->Close();
+        ServerContextManager::UnRegisterScriptContext(scriptContextInfo);
+    }
     // This tells the run-time, when it is marshalling the out
     // parameters, that the context handle has been closed normally.
     *scriptContextInfoAddress = nullptr;
 
-    Assert(scriptContextInfo->IsClosed());
     HeapDelete(scriptContextInfo);
 
     return S_OK;

+ 3 - 3
lib/Runtime/Language/JavascriptOperators.cpp

@@ -6435,7 +6435,7 @@ CommonNumber:
             aParent = CrossSite::MarshalVar(scriptContext, aParent);
             if (i == length)
             {
-                length += 8;
+                length = UInt16Math::Add(length, 8);
                 FrameDisplay * tmp = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*), FrameDisplay, length);
                 js_memcpy_s((char*)tmp + tmp->GetOffsetOfScopes(), tmp->GetLength() * sizeof(void *), (char*)pDisplay + pDisplay->GetOffsetOfScopes(), pDisplay->GetLength() * sizeof(void*));
                 pDisplay = tmp;
@@ -6493,10 +6493,10 @@ CommonNumber:
 
         FrameDisplay *pDisplay = nullptr;
         FrameDisplay *envDisplay = (FrameDisplay*)argEnv;
-        uint16 length = envDisplay->GetLength() + 1;
+        uint16 length = UInt16Math::Add(envDisplay->GetLength(), 1);
 
         pDisplay = RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*), FrameDisplay, length);
-        for (int j = 0; j < length - 1; j++)
+        for (uint16 j = 0; j < length - 1; j++)
         {
             pDisplay->SetItem(j + 1, envDisplay->GetItem(j));
         }

+ 27 - 21
lib/Runtime/Library/JavascriptArray.cpp

@@ -5253,16 +5253,30 @@ Case0:
 
         if (hasInlineSegment)
         {
-            SparseArraySegmentBase* headSegBase = array->head;
-            SparseArraySegment<T>* headSeg = (SparseArraySegment<T>*)headSegBase;
+            AnalysisAssert(array->head);
+            SparseArraySegment<T>* newHeadSeg = array->ReallocNonLeafSegment((SparseArraySegment<T>*)PointerValue(array->head), array->head->next);
+            array->head = newHeadSeg;
+        }
+    }
 
-            AnalysisAssert(headSeg);
-            SparseArraySegment<T>* newHeadSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false>(recycler,
-                headSeg->left, headSeg->length, headSeg->size, headSeg->next);
+    template <typename T>
+    void JavascriptArray::ReallocateNonLeafLastSegmentIfLeaf(JavascriptArray * arr, Recycler * recycler)
+    {
+        Assert(arr->head && arr->head->next); // Doesn't make sense to reallocate a leaf last segment as a non-leaf if its not going to point to any other segments.
 
-            newHeadSeg = SparseArraySegment<T>::CopySegment(recycler, newHeadSeg, headSeg->left, headSeg, headSeg->left, headSeg->length);
-            newHeadSeg->next = headSeg->next;
-            array->head = newHeadSeg;
+        // TODO: Consider utilizing lastUsedSegment once we fix CopyHeadIfInlinedHeadSegment in that respect.
+        SparseArraySegmentBase *lastSeg = nullptr;
+        SparseArraySegmentBase *seg = arr->head;
+        while (seg)
+        {
+            lastSeg = seg;
+            seg = seg->next;
+        }
+
+        if (SparseArraySegmentBase::IsLeafSegment(lastSeg, recycler))
+        {
+            AnalysisAssert(lastSeg);
+            arr->ReallocNonLeafSegment((SparseArraySegment<T>*)lastSeg, lastSeg->next, true /*forceNonLeaf*/);
         }
     }
 
@@ -5369,6 +5383,8 @@ Case0:
             bool isIntArray = false;
             bool isFloatArray = false;
 
+            pArr->ClearSegmentMap(); // Just dump the segment map on reverse
+
             if (JavascriptNativeIntArray::Is(pArr))
             {
                 isIntArray = true;
@@ -5386,10 +5402,12 @@ Case0:
                 if (isIntArray)
                 {
                     CopyHeadIfInlinedHeadSegment<int32>(pArr, recycler);
+                    ReallocateNonLeafLastSegmentIfLeaf<int32>(pArr, recycler);
                 }
                 else if (isFloatArray)
                 {
                     CopyHeadIfInlinedHeadSegment<double>(pArr, recycler);
+                    ReallocateNonLeafLastSegmentIfLeaf<double>(pArr, recycler);
                 }
                 else
                 {
@@ -5437,24 +5455,14 @@ Case0:
             }
 
             pArr->head = prevSeg;
-
-            // Just dump the segment map on reverse
-            pArr->ClearSegmentMap();
+            pArr->InvalidateLastUsedSegment(); // lastUsedSegment might be 0-length and discarded above
 
             if (isIntArray)
             {
-                if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
-                {
-                    pArr->ReallocNonLeafSegment(SparseArraySegment<int32>::From(pArr->head), pArr->head->next);
-                }
                 pArr->EnsureHeadStartsFromZero<int32>(recycler);
             }
             else if (isFloatArray)
             {
-                if (pArr->head && pArr->head->next && SparseArraySegmentBase::IsLeafSegment(pArr->head, recycler))
-                {
-                    pArr->ReallocNonLeafSegment(SparseArraySegment<double>::From(pArr->head), pArr->head->next);
-                }
                 pArr->EnsureHeadStartsFromZero<double>(recycler);
             }
             else
@@ -5462,8 +5470,6 @@ Case0:
                 pArr->EnsureHeadStartsFromZero<Var>(recycler);
             }
 
-            pArr->InvalidateLastUsedSegment(); // lastUsedSegment might be 0-length and discarded above
-
 #ifdef VALIDATE_ARRAY
             pArr->ValidateArray();
 #endif

+ 3 - 1
lib/Runtime/Library/JavascriptArray.h

@@ -417,7 +417,7 @@ namespace Js
         JavascriptArray(JavascriptArray * instance, bool boxHead);
 
         template<typename T> inline void LinkSegments(SparseArraySegment<T>* prev, SparseArraySegment<T>* current);
-        template<typename T> inline SparseArraySegment<T>* ReallocNonLeafSegment(SparseArraySegment<T>* seg, SparseArraySegmentBase* nextSeg);
+        template<typename T> inline SparseArraySegment<T>* ReallocNonLeafSegment(SparseArraySegment<T>* seg, SparseArraySegmentBase* nextSeg, bool forceNonLeaf = false);
         void TryAddToSegmentMap(Recycler* recycler, SparseArraySegmentBase* seg);
 
     private:
@@ -575,6 +575,8 @@ namespace Js
 
         template<typename T>
         static void CopyHeadIfInlinedHeadSegment(JavascriptArray *array, Recycler *recycler);
+        template<typename T>
+        static void ReallocateNonLeafLastSegmentIfLeaf(JavascriptArray * arr, Recycler * recycler);
 
         template<typename T>
         static void ArraySpliceHelper(JavascriptArray* pNewArr, JavascriptArray* pArr, uint32 start, uint32 deleteLen,

+ 10 - 2
lib/Runtime/Library/JavascriptArray.inl

@@ -95,7 +95,7 @@ namespace Js
     }
 
     template<typename T>
-    inline SparseArraySegment<T>* JavascriptArray::ReallocNonLeafSegment(SparseArraySegment<T> *seg, SparseArraySegmentBase* nextSeg)
+    inline SparseArraySegment<T>* JavascriptArray::ReallocNonLeafSegment(SparseArraySegment<T> *seg, SparseArraySegmentBase* nextSeg, bool forceNonLeaf)
     {
         // Find the segment prior to seg.
         SparseArraySegmentBase *prior = nullptr;
@@ -106,8 +106,16 @@ namespace Js
                 Assert(prior->next);
             }
         }
+        SparseArraySegment<T> *newSeg = nullptr;
         Recycler *recycler = this->GetScriptContext()->GetRecycler();
-        SparseArraySegment<T> *newSeg = SparseArraySegment<T>::AllocateSegment(recycler, seg->left, seg->length, nextSeg);
+        if (forceNonLeaf)
+        {
+            newSeg = SparseArraySegment<T>::template AllocateSegmentImpl<false /*isLeaf*/>(recycler, seg->left, seg->length, nextSeg);
+        }
+        else
+        {
+            newSeg = SparseArraySegment<T>::AllocateSegment(recycler, seg->left, seg->length, nextSeg);
+        }
         CopyArray(newSeg->elements, seg->length, seg->elements, seg->length);
 
         LinkSegmentsCommon(prior, newSeg);

+ 5 - 1
lib/Runtime/Types/DictionaryPropertyDescriptor.h

@@ -154,6 +154,10 @@ namespace Js
         if (this->IsAccessor)
         {
             Assert(this->Data == NoSlots);
+            if (addingLetConstGlobal)
+            {
+                this->Data = nextPropertyIndex++;
+            }
         }
         else if (addingLetConstGlobal)
         {
@@ -165,7 +169,7 @@ namespace Js
             this->Getter = nextPropertyIndex++;
         }
         this->Attributes |= PropertyLetConstGlobal;
-        Assert(GetDataPropertyIndex<false>() != NoSlots);
+        Assert((addingLetConstGlobal ? GetDataPropertyIndex<true>() : GetDataPropertyIndex<false>()) != NoSlots);
     }
 
     template <typename TPropertyIndex>

+ 2 - 1
lib/Runtime/Types/SimpleDictionaryTypeHandler.cpp

@@ -563,7 +563,7 @@ namespace Js
 
         if(isUnordered)
         {
-            newTypeHandler->CopyUnorderedStateFrom(*AsUnordered());
+            newTypeHandler->CopyUnorderedStateFrom(*AsUnordered(), instance);
         }
         else
         {
@@ -2685,6 +2685,7 @@ namespace Js
                 ->AddProperty(instance, propertyKey, value, attributes, info, flags, possibleSideEffects);
         }
 
+        // CONSIDER: Do this after TryReuseDeletedPropertyIndex. If we can reuse slots, no need to grow right now.
         if (this->GetSlotCapacity() <= nextPropertyIndex)
         {
             if (this->GetSlotCapacity() >= MaxPropertyIndexSize)

+ 19 - 1
lib/Runtime/Types/SimpleDictionaryUnorderedTypeHandler.h

@@ -43,12 +43,30 @@ namespace Js
 
     private:
         template<class OtherTPropertyIndex, class OtherTMapKey, bool OtherIsNotExtensibleSupported>
-        void CopyUnorderedStateFrom(const SimpleDictionaryUnorderedTypeHandler<OtherTPropertyIndex, OtherTMapKey, OtherIsNotExtensibleSupported> &other)
+        void CopyUnorderedStateFrom(const SimpleDictionaryUnorderedTypeHandler<OtherTPropertyIndex, OtherTMapKey, OtherIsNotExtensibleSupported> &other,
+            DynamicObject *const object)
         {
             CompileAssert(sizeof(TPropertyIndex) >= sizeof(OtherTPropertyIndex));
             if (other.deletedPropertyIndex != PropertyIndexRanges<OtherTPropertyIndex>::NoSlots)
             {
                 deletedPropertyIndex = other.deletedPropertyIndex;
+
+                // If terminator values are different, walk to end of chain and update terminator value
+                if ((int)PropertyIndexRanges<TPropertyIndex>::NoSlots != (int)PropertyIndexRanges<OtherTPropertyIndex>::NoSlots)
+                {
+                    OtherTPropertyIndex cur = other.deletedPropertyIndex;
+                    for (;;)
+                    {
+                        OtherTPropertyIndex next = static_cast<OtherTPropertyIndex>(TaggedInt::ToInt32(object->GetSlot(cur)));
+                        if (next == PropertyIndexRanges<OtherTPropertyIndex>::NoSlots)
+                        {
+                            this->SetSlotUnchecked(object, cur, TaggedInt::ToVarUnchecked(PropertyIndexRanges<TPropertyIndex>::NoSlots));
+                            break;
+                        }
+
+                        cur = next;
+                    }
+                }
             }
         }
 

+ 5 - 0
test/LetConst/rlexe.xml

@@ -379,4 +379,9 @@
       <compile-flags>-args summary -endargs</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>shadowedsetter.js</files>
+    </default>
+  </test>
 </regress-exe>

+ 18 - 0
test/LetConst/shadowedsetter.js

@@ -0,0 +1,18 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+evaluate = WScript.LoadScript;
+
+__defineSetter__("x", function () { });
+
+evaluate(`
+  let x = 'let';
+  Object.defineProperty(this, "x", { value:
+          0xdec0  })
+  if (x === 'let' && this.x === 57024)
+  {
+    WScript.Echo('pass');
+  }
+`);