From af07d28d486843bbd1fdefe742a9c090026b32ee Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Thu, 8 Mar 2018 17:36:51 -0800 Subject: [PATCH 1/8] [CVE-2018-0980] Chakra JIT - A bound check elimination bug - Google, Inc. --- lib/Backend/GlobOpt.cpp | 39 +++++++++++++++++++++++++++++++++++---- lib/Backend/GlobOpt.h | 5 ++++- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index d58c4b97db3..76daa7f7e78 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -14402,7 +14402,8 @@ GlobOpt::OptArraySrc(IR::Instr * *const instrRef) currentBlock->next, hoistBlock, hoistInfo.IndexSym(), - hoistInfo.IndexValueNumber()); + hoistInfo.IndexValueNumber(), + true); it.IsValid(); it.MoveNext()) { @@ -14670,7 +14671,7 @@ GlobOpt::OptArraySrc(IR::Instr * *const instrRef) Assert(!hoistInfo.Loop() || hoistBlock != currentBlock); if(hoistBlock != currentBlock) { - for(InvariantBlockBackwardIterator it(this, currentBlock->next, hoistBlock, nullptr); + for(InvariantBlockBackwardIterator it(this, currentBlock->next, hoistBlock, nullptr, InvalidValueNumber, true); it.IsValid(); it.MoveNext()) { @@ -17116,12 +17117,15 @@ InvariantBlockBackwardIterator::InvariantBlockBackwardIterator( BasicBlock *const exclusiveBeginBlock, BasicBlock *const inclusiveEndBlock, StackSym *const invariantSym, - const ValueNumber invariantSymValueNumber) + const ValueNumber invariantSymValueNumber, + bool followFlow) : globOpt(globOpt), exclusiveEndBlock(inclusiveEndBlock->prev), invariantSym(invariantSym), invariantSymValueNumber(invariantSymValueNumber), - block(exclusiveBeginBlock) + block(exclusiveBeginBlock), + blockBV(globOpt->tempAlloc), + followFlow(followFlow) #if DBG , inclusiveEndBlock(inclusiveEndBlock) @@ -17159,6 +17163,11 @@ InvariantBlockBackwardIterator::MoveNext() break; } + if (!this->UpdatePredBlockBV()) + { + continue; + } + if(block->isDeleted) { continue; @@ -17186,6 +17195,28 @@ InvariantBlockBackwardIterator::MoveNext() } } +bool +InvariantBlockBackwardIterator::UpdatePredBlockBV() +{ + if (!this->followFlow) + { + return true; + } + + // Track blocks we've visited to ensure that we only iterate over predecessor blocks + if (!this->blockBV.IsEmpty() && !this->blockBV.Test(this->block->GetBlockNum())) + { + return false; + } + + FOREACH_SLISTBASECOUNTED_ENTRY(FlowEdge*, edge, this->block->GetPredList()) + { + this->blockBV.Set(edge->GetPred()->GetBlockNum()); + } NEXT_SLISTBASECOUNTED_ENTRY; + + return true; +} + BasicBlock * InvariantBlockBackwardIterator::Block() const { diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h index c21f3d67aa5..0c0d2b8f0a0 100644 --- a/lib/Backend/GlobOpt.h +++ b/lib/Backend/GlobOpt.h @@ -364,13 +364,16 @@ class InvariantBlockBackwardIterator const ValueNumber invariantSymValueNumber; BasicBlock *block; Value *invariantSymValue; + BVSparse blockBV; + bool followFlow; #if DBG BasicBlock *const inclusiveEndBlock; #endif + bool UpdatePredBlockBV(); public: - InvariantBlockBackwardIterator(GlobOpt *const globOpt, BasicBlock *const exclusiveBeginBlock, BasicBlock *const inclusiveEndBlock, StackSym *const invariantSym, const ValueNumber invariantSymValueNumber = InvalidValueNumber); + InvariantBlockBackwardIterator(GlobOpt *const globOpt, BasicBlock *const exclusiveBeginBlock, BasicBlock *const inclusiveEndBlock, StackSym *const invariantSym, const ValueNumber invariantSymValueNumber = InvalidValueNumber, bool followFlow = false); public: bool IsValid() const; From 14a2773eeefb39a6c89c0f6a80072f3cc4b06a78 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Thu, 8 Mar 2018 10:36:47 -0800 Subject: [PATCH 2/8] [CVE-2018-1019] Use after free of JavascriptExceptionObject - Individual --- lib/Runtime/Base/ThreadContext.cpp | 2 +- lib/Runtime/Base/ThreadContext.h | 8 +- .../Language/JavascriptExceptionOperators.cpp | 117 +++++++++++------- .../Language/JavascriptExceptionOperators.h | 10 ++ 4 files changed, 85 insertions(+), 52 deletions(-) diff --git a/lib/Runtime/Base/ThreadContext.cpp b/lib/Runtime/Base/ThreadContext.cpp index 0d6d3e92f14..eace63e2a00 100644 --- a/lib/Runtime/Base/ThreadContext.cpp +++ b/lib/Runtime/Base/ThreadContext.cpp @@ -72,6 +72,7 @@ const Js::PropertyRecord * const ThreadContext::builtInPropertyRecords[] = }; ThreadContext::RecyclableData::RecyclableData(Recycler *const recycler) : + pendingFinallyException(nullptr), soErrorObject(nullptr, nullptr, nullptr, true), oomErrorObject(nullptr, nullptr, nullptr, true), terminatedErrorObject(nullptr, nullptr, nullptr), @@ -94,7 +95,6 @@ ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager, isThreadBound(false), hasThrownPendingException(false), hasBailedOutBitPtr(nullptr), - pendingFinallyException(nullptr), noScriptScope(false), heapEnum(nullptr), threadContextFlags(ThreadContextFlagNoFlag), diff --git a/lib/Runtime/Base/ThreadContext.h b/lib/Runtime/Base/ThreadContext.h index 21bc7b1ed46..ed89c7ffdcc 100644 --- a/lib/Runtime/Base/ThreadContext.h +++ b/lib/Runtime/Base/ThreadContext.h @@ -441,8 +441,6 @@ class ThreadContext sealed : private: const Js::PropertyRecord * emptyStringPropertyRecord; - - Js::JavascriptExceptionObject * pendingFinallyException; bool noScriptScope; #ifdef ENABLE_SCRIPT_DEBUGGING @@ -557,6 +555,8 @@ class ThreadContext sealed : Field(Js::TempArenaAllocatorObject *) temporaryArenaAllocators[MaxTemporaryArenaAllocators]; Field(Js::TempGuestArenaAllocatorObject *) temporaryGuestArenaAllocators[MaxTemporaryArenaAllocators]; + Field(Js::JavascriptExceptionObject *) pendingFinallyException; + Field(Js::JavascriptExceptionObject *) exceptionObject; Field(bool) propagateException; @@ -1292,12 +1292,12 @@ class ThreadContext sealed : void SetPendingFinallyException(Js::JavascriptExceptionObject * exceptionObj) { - pendingFinallyException = exceptionObj; + recyclableData->pendingFinallyException = exceptionObj; } Js::JavascriptExceptionObject * GetPendingFinallyException() { - return pendingFinallyException; + return recyclableData->pendingFinallyException; } Js::EntryPointInfo ** RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint); diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.cpp b/lib/Runtime/Language/JavascriptExceptionOperators.cpp index 452bd169686..28ed1779409 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.cpp +++ b/lib/Runtime/Language/JavascriptExceptionOperators.cpp @@ -72,6 +72,17 @@ namespace Js m_threadContext->SetTryCatchFrameAddr(m_prevTryCatchFrameAddr); } + JavascriptExceptionOperators::PendingFinallyExceptionStack::PendingFinallyExceptionStack(ScriptContext* scriptContext, Js::JavascriptExceptionObject *exceptionObj) + { + m_threadContext = scriptContext->GetThreadContext(); + m_threadContext->SetPendingFinallyException(exceptionObj); + } + + JavascriptExceptionOperators::PendingFinallyExceptionStack::~PendingFinallyExceptionStack() + { + m_threadContext->SetPendingFinallyException(nullptr); + } + bool JavascriptExceptionOperators::CrawlStackForWER(Js::ScriptContext& scriptContext) { return Js::Configuration::Global.flags.WERExceptionSupport && !scriptContext.GetThreadContext()->HasCatchHandler(); @@ -199,9 +210,11 @@ namespace Js JavascriptExceptionOperators::DoThrow(exception, scriptContext); } - scriptContext->GetThreadContext()->SetPendingFinallyException(exception); - void *continuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize, exception); - return continuation; + { + Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, exception); + void *continuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize, exception); + return continuation; + } } scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); @@ -371,13 +384,15 @@ namespace Js JavascriptExceptionOperators::DoThrow(exception, scriptContext); } - scriptContext->GetThreadContext()->SetPendingFinallyException(exception); + { + Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, exception); #if defined(_M_ARM) - void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); + void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); #elif defined(_M_ARM64) - void * finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); + void * finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize); #endif - return finallyContinuation; + return finallyContinuation; + } } scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); @@ -699,60 +714,68 @@ namespace Js JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); } - scriptContext->GetThreadContext()->SetPendingFinallyException(pExceptionObject); + { + Js::JavascriptExceptionOperators::PendingFinallyExceptionStack pendingFinallyExceptionStack(scriptContext, pExceptionObject); - void* newContinuationAddr = NULL; + if (!tryAddr) + { + // Bug in compiler optimizer: dtor is not called, it is a compiler bug + // The compiler thinks the asm cannot throw, so add an explicit throw to generate dtor calls + Js::Throw::InternalError(); + } + void* newContinuationAddr = NULL; #ifdef _M_IX86 - void *savedEsp; + void *savedEsp; - __asm - { - // Save and restore the callee-saved registers around the call. - // TODO: track register kills by region and generate per-region prologs and epilogs - push esi - push edi - push ebx + __asm + { + // Save and restore the callee-saved registers around the call. + // TODO: track register kills by region and generate per-region prologs and epilogs + push esi + push edi + push ebx - // 8-byte align frame to improve floating point perf of our JIT'd code. - // Save ESP - mov ecx, esp - mov savedEsp, ecx - and esp, -8 + // 8-byte align frame to improve floating point perf of our JIT'd code. + // Save ESP + mov ecx, esp + mov savedEsp, ecx + and esp, -8 - // Set up the call target - mov eax, handlerAddr + // Set up the call target + mov eax, handlerAddr #if 0 && defined(_CONTROL_FLOW_GUARD) - // verify that the call target is valid - mov ebx, eax; save call target - mov ecx, eax - call[__guard_check_icall_fptr] - mov eax, ebx; restore call target + // verify that the call target is valid + mov ebx, eax; save call target + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ebx; restore call target #endif - // save the current frame ptr, and adjust the frame to access - // locals in native code. - push ebp - mov ebp, framePtr - call eax - pop ebp + // save the current frame ptr, and adjust the frame to access + // locals in native code. + push ebp + mov ebp, framePtr + call eax + pop ebp - // The native code gives us the address where execution should continue on exit - // from the finally, but only if flow leaves the finally before it completes. - mov newContinuationAddr, eax + // The native code gives us the address where execution should continue on exit + // from the finally, but only if flow leaves the finally before it completes. + mov newContinuationAddr, eax - // Restore ESP - mov ecx, savedEsp - mov esp, ecx + // Restore ESP + mov ecx, savedEsp + mov esp, ecx - pop ebx - pop edi - pop esi - } + pop ebx + pop edi + pop esi + } #else - AssertMsg(FALSE, "Unsupported native try-finally handler"); + AssertMsg(FALSE, "Unsupported native try-finally handler"); #endif - return newContinuationAddr; + return newContinuationAddr; + } } scriptContext->GetThreadContext()->SetHasBailedOutBitPtr(nullptr); diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.h b/lib/Runtime/Language/JavascriptExceptionOperators.h index f32f7065dae..79a87542fca 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.h +++ b/lib/Runtime/Language/JavascriptExceptionOperators.h @@ -54,6 +54,16 @@ namespace Js ~TryCatchFrameAddrStack(); }; + class PendingFinallyExceptionStack + { + private: + ThreadContext* m_threadContext; + + public: + PendingFinallyExceptionStack(ScriptContext* scriptContext, Js::JavascriptExceptionObject *exceptionObj); + ~PendingFinallyExceptionStack(); + }; + static void __declspec(noreturn) OP_Throw(Var object, ScriptContext* scriptContext); static void __declspec(noreturn) Throw(Var object, ScriptContext* scriptContext); static void __declspec(noreturn) ThrowExceptionObject(Js::JavascriptExceptionObject* exceptionObject, ScriptContext* scriptContext, bool considerPassingToDebugger = false, PVOID returnAddress = NULL, bool resetStack = false); From 396d78f60a3d8444218253523365965f97942225 Mon Sep 17 00:00:00 2001 From: Jianchun Xu Date: Fri, 16 Feb 2018 14:40:01 -0800 Subject: [PATCH 3/8] [CVE-2018-0995] Edge - JavascriptNativeIntArray::ConvertToVarArray could lead to UAF - Individual --- lib/Runtime/Library/JavascriptArray.cpp | 70 ++++++++++++++----------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index c23b0836cd5..8e87c026092 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -1921,8 +1921,21 @@ namespace Js // And/or the old segment is not scanned by the recycler, so we need a new one to hold vars. SparseArraySegment *newSeg = SparseArraySegment::AllocateSegment(recycler, left, length, nextSeg); - AnalysisAssert(newSeg); + + // Fill the new segment with the overflow. + for (i = 0; (uint)i < newSeg->length; i++) + { + ival = ((SparseArraySegment*)seg)->elements[i]; + if (ival == JavascriptNativeIntArray::MissingItem) + { + continue; + } + newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext); + } + + // seg elements are copied over, now it is safe to replace seg with newSeg. + // seg could be GC collected if replaced by newSeg. Assert((prevSeg == nullptr) == (seg == intArray->head)); newSeg->next = nextSeg; intArray->LinkSegments((SparseArraySegment*)prevSeg, newSeg); @@ -1937,17 +1950,6 @@ namespace Js { segmentMap->SwapSegment(left, seg, newSeg); } - - // Fill the new segment with the overflow. - for (i = 0; (uint)i < newSeg->length; i++) - { - ival = ((SparseArraySegment*)seg)->elements[i]; - if (ival == JavascriptNativeIntArray::MissingItem) - { - continue; - } - newSeg->elements[i] = JavascriptNumber::ToVar(ival, scriptContext); - } } else { @@ -2097,26 +2099,12 @@ namespace Js } uint32 left = seg->left; uint32 length = seg->length; - SparseArraySegment *newSeg; + SparseArraySegment *newSeg = nullptr; if (seg->next == nullptr && SparseArraySegmentBase::IsLeafSegment(seg, recycler)) { // The old segment is not scanned by the recycler, so we need a new one to hold vars. newSeg = SparseArraySegment::AllocateSegment(recycler, left, length, nextSeg); - Assert((prevSeg == nullptr) == (seg == fArray->head)); - newSeg->next = nextSeg; - fArray->LinkSegments((SparseArraySegment*)prevSeg, newSeg); - if (fArray->GetLastUsedSegment() == seg) - { - fArray->SetLastUsedSegment(newSeg); - } - prevSeg = newSeg; - - SegmentBTree * segmentMap = fArray->GetSegmentMap(); - if (segmentMap) - { - segmentMap->SwapSegment(left, seg, newSeg); - } } else { @@ -2172,6 +2160,26 @@ namespace Js // Fill the remaining slots. newSeg->FillSegmentBuffer(i, seg->size); } + + // seg elements are copied over, now it is safe to replace seg with newSeg. + // seg could be GC collected if replaced by newSeg. + if (newSeg != seg) + { + Assert((prevSeg == nullptr) == (seg == fArray->head)); + newSeg->next = nextSeg; + fArray->LinkSegments((SparseArraySegment*)prevSeg, newSeg); + if (fArray->GetLastUsedSegment() == seg) + { + fArray->SetLastUsedSegment(newSeg); + } + prevSeg = newSeg; + + SegmentBTree * segmentMap = fArray->GetSegmentMap(); + if (segmentMap) + { + segmentMap->SwapSegment(left, seg, newSeg); + } + } } if (fArray->GetType() == scriptContext->GetLibrary()->GetNativeFloatArrayType()) @@ -3140,7 +3148,7 @@ namespace Js JS_REENTRANT_NO_MUTATE(jsReentLock, CopyNativeIntArrayElementsToVar(pDestArray, BigIndex(idxDest).GetSmallIndex(), pIntItemArray)); idxDest = idxDest + pIntItemArray->length; } - else + else { JavascriptNativeFloatArray *pFloatItemArray = JavascriptOperators::TryFromVar(aItem); if (pFloatItemArray) @@ -3390,7 +3398,7 @@ namespace Js idxDest = idxDest + pIntItemArray->length; } - else + else { JavascriptNativeFloatArray * pFloatItemArray = JavascriptOperators::TryFromVar(aItem); if (pFloatItemArray && !isFillFromPrototypes) @@ -5384,7 +5392,7 @@ namespace Js { RecyclableObject* protoObj = prototype; - if (!(DynamicObject::IsAnyArray(protoObj) || JavascriptOperators::IsObject(protoObj)) + if (!(DynamicObject::IsAnyArray(protoObj) || JavascriptOperators::IsObject(protoObj)) || JavascriptProxy::Is(protoObj) || protoObj->IsExternal()) { @@ -6099,7 +6107,7 @@ namespace Js *isIntArray = true; #endif } - else + else { JavascriptNativeFloatArray* nativeFloatArray = JavascriptOperators::TryFromVar(this); if (nativeFloatArray) From 4bdd0efb1c309a0307cdfbd2856ca8cd88b154bb Mon Sep 17 00:00:00 2001 From: "Thomas Moore (CHAKRA)" Date: Wed, 28 Mar 2018 09:36:39 -0700 Subject: [PATCH 4/8] [CVE-2018-0993] Edge - Stack-to-heap copy bug in Edge,expose internal property - Qihoo 360 --- lib/Runtime/Language/JavascriptOperators.cpp | 4 +- lib/Runtime/Library/JavascriptArray.cpp | 57 +++++++++++++++--- lib/Runtime/Library/JavascriptArray.h | 4 +- .../Library/JavascriptRegularExpression.cpp | 13 ++-- .../Library/JavascriptRegularExpression.h | 4 +- lib/Runtime/Types/ArrayObject.cpp | 4 +- lib/Runtime/Types/ArrayObject.h | 2 +- lib/Runtime/Types/DynamicObject.cpp | 60 +++++++++++++++++-- lib/Runtime/Types/DynamicObject.h | 31 ++++++++-- 9 files changed, 148 insertions(+), 31 deletions(-) diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 89e2ff17154..9e1787e9e87 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -9499,9 +9499,9 @@ namespace Js case Js::TypeIds_Integer: return instance; case Js::TypeIds_RegEx: - return JavascriptRegExp::BoxStackInstance(JavascriptRegExp::FromVar(instance)); + return JavascriptRegExp::BoxStackInstance(JavascriptRegExp::FromVar(instance), deepCopy); case Js::TypeIds_Object: - return DynamicObject::BoxStackInstance(DynamicObject::FromVar(instance)); + return DynamicObject::BoxStackInstance(DynamicObject::FromVar(instance), deepCopy); case Js::TypeIds_Array: return JavascriptArray::BoxStackInstance(JavascriptArray::UnsafeFromVar(instance), deepCopy); case Js::TypeIds_NativeIntArray: diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index 8e87c026092..e03734a72c2 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -11668,7 +11668,7 @@ namespace Js } JavascriptArray::JavascriptArray(JavascriptArray * instance, bool boxHead, bool deepCopy) - : ArrayObject(instance) + : ArrayObject(instance, deepCopy) { if (boxHead) { @@ -11683,6 +11683,40 @@ namespace Js } } + // Allocate a new Array with its own segments and copy the data in instance + // into the new Array + template + T * JavascriptArray::DeepCopyInstance(T * instance) + { + return RecyclerNewPlusZ(instance->GetRecycler(), + instance->GetTypeHandler()->GetInlineSlotsSize() + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), + T, instance, true /*boxHead*/, true /*deepCopy*/); + } + + ArrayObject* JavascriptArray::DeepCopyInstance(ArrayObject* arrayObject) + { + ArrayObject* arrayCopy; + TypeId typeId = JavascriptOperators::GetTypeId(arrayObject); + switch (typeId) + { + case Js::TypeIds_Array: + arrayCopy = JavascriptArray::DeepCopyInstance(JavascriptArray::UnsafeFromVar(arrayObject)); + break; + case Js::TypeIds_NativeIntArray: + arrayCopy = JavascriptArray::DeepCopyInstance(JavascriptNativeIntArray::UnsafeFromVar(arrayObject)); + break; + case Js::TypeIds_NativeFloatArray: + arrayCopy = JavascriptArray::DeepCopyInstance(JavascriptNativeFloatArray::UnsafeFromVar(arrayObject)); + break; + + default: + AssertAndFailFast(!"Unexpected objectArray type while boxing stack instance"); + arrayCopy = nullptr; + }; + + return arrayCopy; + } + template T * JavascriptArray::BoxStackInstance(T * instance, bool deepCopy) { @@ -11713,9 +11747,16 @@ namespace Js // Reallocate both the object as well as the head segment when the head is on the stack or // when a deep copy is needed. This is to prevent a scenario where box may leave either one // on the stack when both must be on the heap. - boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), - inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), - T, instance, true, deepCopy); + if (deepCopy) + { + boxedInstance = DeepCopyInstance(instance); + } + else + { + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), + inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), + T, instance, true /*boxHead*/, false /*deepCopy*/); + } } else if(inlineSlotsSize) { @@ -11803,14 +11844,14 @@ namespace Js } #endif - JavascriptNativeArray::JavascriptNativeArray(JavascriptNativeArray * instance) : - JavascriptArray(instance, false, false), + JavascriptNativeArray::JavascriptNativeArray(JavascriptNativeArray * instance, bool deepCopy) : + JavascriptArray(instance, false, deepCopy), weakRefToFuncBody(instance->weakRefToFuncBody) { } JavascriptNativeIntArray::JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead, bool deepCopy) : - JavascriptNativeArray(instance) + JavascriptNativeArray(instance, deepCopy) { if (boxHead) { @@ -11856,7 +11897,7 @@ namespace Js #endif JavascriptNativeFloatArray::JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead, bool deepCopy) : - JavascriptNativeArray(instance) + JavascriptNativeArray(instance, deepCopy) { if (boxHead) { diff --git a/lib/Runtime/Library/JavascriptArray.h b/lib/Runtime/Library/JavascriptArray.h index a121f389bfb..f1c7c041a70 100644 --- a/lib/Runtime/Library/JavascriptArray.h +++ b/lib/Runtime/Library/JavascriptArray.h @@ -899,10 +899,12 @@ namespace Js static uint32 GetSpreadArgLen(Var spreadArg, ScriptContext *scriptContext); static JavascriptArray * BoxStackInstance(JavascriptArray * instance, bool deepCopy); + static ArrayObject * DeepCopyInstance(ArrayObject * instance); protected: template void InitBoxedInlineSegments(SparseArraySegment * dst, SparseArraySegment * src, bool deepCopy); template static T * BoxStackInstance(T * instance, bool deepCopy); + template static T * DeepCopyInstance(T * instance); public: template static size_t DetermineAllocationSize(const uint inlineElementSlots, size_t *const allocationPlusSizeRef = nullptr, uint *const alignedInlineElementSlotsRef = nullptr); @@ -960,7 +962,7 @@ namespace Js JavascriptArray(length, type), weakRefToFuncBody(nullptr) {} // For BoxStackInstance - JavascriptNativeArray(JavascriptNativeArray * instance); + JavascriptNativeArray(JavascriptNativeArray * instance, bool deepCopy); Field(RecyclerWeakReference *) weakRefToFuncBody; diff --git a/lib/Runtime/Library/JavascriptRegularExpression.cpp b/lib/Runtime/Library/JavascriptRegularExpression.cpp index c8ad888168d..8de7ce5b792 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.cpp +++ b/lib/Runtime/Library/JavascriptRegularExpression.cpp @@ -54,8 +54,8 @@ namespace Js #endif } - JavascriptRegExp::JavascriptRegExp(JavascriptRegExp * instance) : - DynamicObject(instance), + JavascriptRegExp::JavascriptRegExp(JavascriptRegExp * instance, bool deepCopy) : + DynamicObject(instance, deepCopy), pattern(instance->GetPattern()), splitPattern(instance->GetSplitPattern()), lastIndexVar(instance->lastIndexVar), @@ -63,6 +63,11 @@ namespace Js { // For boxing stack instance Assert(ThreadContext::IsOnStack(instance)); + + // These members should never be on the stack and thus never need to be deep copied + Assert(!ThreadContext::IsOnStack(instance->GetPattern())); + Assert(!ThreadContext::IsOnStack(instance->GetSplitPattern())); + Assert(!ThreadContext::IsOnStack(instance->lastIndexVar)); } bool JavascriptRegExp::Is(Var aValue) @@ -1057,7 +1062,7 @@ namespace Js DEFINE_FLAG_GETTER(EntryGetterSticky, sticky, IsSticky) DEFINE_FLAG_GETTER(EntryGetterUnicode, unicode, IsUnicode) - JavascriptRegExp * JavascriptRegExp::BoxStackInstance(JavascriptRegExp * instance) + JavascriptRegExp * JavascriptRegExp::BoxStackInstance(JavascriptRegExp * instance, bool deepCopy) { Assert(ThreadContext::IsOnStack(instance)); // On the stack, the we reserved a pointer before the object as to store the boxed value @@ -1068,7 +1073,7 @@ namespace Js return boxedInstance; } Assert(instance->GetTypeHandler()->GetInlineSlotsSize() == 0); - boxedInstance = RecyclerNew(instance->GetRecycler(), JavascriptRegExp, instance); + boxedInstance = RecyclerNew(instance->GetRecycler(), JavascriptRegExp, instance, deepCopy); *boxedInstanceRef = boxedInstance; return boxedInstance; } diff --git a/lib/Runtime/Library/JavascriptRegularExpression.h b/lib/Runtime/Library/JavascriptRegularExpression.h index 45ebe1ea1c2..26c97b9a991 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.h +++ b/lib/Runtime/Library/JavascriptRegularExpression.h @@ -82,7 +82,7 @@ namespace Js UnifiedRegex::RegexFlags SetRegexFlag(PropertyId propertyId, UnifiedRegex::RegexFlags flags, UnifiedRegex::RegexFlags flag, ScriptContext* scriptContext); // For boxing stack instance - JavascriptRegExp(JavascriptRegExp * instance); + JavascriptRegExp(JavascriptRegExp * instance, bool deepCopy); DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptRegExp); protected: @@ -201,7 +201,7 @@ namespace Js virtual uint GetSpecialPropertyCount() const override; virtual PropertyId const * GetSpecialPropertyIds() const override; - static Js::JavascriptRegExp * BoxStackInstance(Js::JavascriptRegExp * instance); + static Js::JavascriptRegExp * BoxStackInstance(Js::JavascriptRegExp * instance, bool deepCopy); #if ENABLE_TTD public: diff --git a/lib/Runtime/Types/ArrayObject.cpp b/lib/Runtime/Types/ArrayObject.cpp index bad3ecc2f5b..893088127c4 100644 --- a/lib/Runtime/Types/ArrayObject.cpp +++ b/lib/Runtime/Types/ArrayObject.cpp @@ -10,8 +10,8 @@ namespace Js { - ArrayObject::ArrayObject(ArrayObject * instance) - : DynamicObject(instance), + ArrayObject::ArrayObject(ArrayObject * instance, bool deepCopy) + : DynamicObject(instance, deepCopy), length(instance->length) { } diff --git a/lib/Runtime/Types/ArrayObject.h b/lib/Runtime/Types/ArrayObject.h index f50f0d10b45..617b4845e23 100644 --- a/lib/Runtime/Types/ArrayObject.h +++ b/lib/Runtime/Types/ArrayObject.h @@ -34,7 +34,7 @@ namespace Js } // For boxing stack instance - ArrayObject(ArrayObject * instance); + ArrayObject(ArrayObject * instance, bool deepCopy); void __declspec(noreturn) ThrowItemNotConfigurableError(PropertyId propId = Constants::NoProperty); void VerifySetItemAttributes(PropertyId propId, PropertyAttributes attributes); diff --git a/lib/Runtime/Types/DynamicObject.cpp b/lib/Runtime/Types/DynamicObject.cpp index bc1abf7bb68..04fb743a314 100644 --- a/lib/Runtime/Types/DynamicObject.cpp +++ b/lib/Runtime/Types/DynamicObject.cpp @@ -46,7 +46,7 @@ namespace Js #endif } - DynamicObject::DynamicObject(DynamicObject * instance) : + DynamicObject::DynamicObject(DynamicObject * instance, bool deepCopy) : RecyclableObject(instance->type), auxSlots(instance->auxSlots), objectArray(instance->objectArray) // copying the array should copy the array flags and array call site index as well @@ -63,6 +63,8 @@ namespace Js #if !FLOATVAR ScriptContext * scriptContext = this->GetScriptContext(); #endif + // Copy the inline slot data from the source instance. Deep copy is implicit because + // the inline slot allocation is already accounted for with the allocation of the object. for (int i = 0; i < inlineSlotCount; i++) { #if !FLOATVAR @@ -71,11 +73,27 @@ namespace Js #else dstSlots[i] = srcSlots[i]; #endif - + Assert(!ThreadContext::IsOnStack(dstSlots[i])); } if (propertyCount > inlineSlotCapacity) { + // Properties that are not inlined are stored in the auxSlots, which must be copied + // from the source instance. + + // Assert that this block of code will not overwrite inline slot data + Assert(!typeHandler->IsObjectHeaderInlinedTypeHandler()); + + if (deepCopy) + { + // When a deepCopy is needed, ensure that auxSlots is not shared with the source instance + // so that both objects can have their own, separate lifetimes. + InitSlots(this); + + // This auxSlots should now be a separate allocation. + Assert(auxSlots != instance->auxSlots); + } + uint auxSlotCount = propertyCount - inlineSlotCapacity; for (uint i = 0; i < auxSlotCount; i++) @@ -84,11 +102,43 @@ namespace Js // Currently we only support temp numbers assigned to stack objects auxSlots[i] = JavascriptNumber::BoxStackNumber(instance->auxSlots[i], scriptContext); #else + // Copy the slot values from that instance to this + Assert(!ThreadContext::IsOnStack(instance->auxSlots[i])); auxSlots[i] = instance->auxSlots[i]; #endif + Assert(!ThreadContext::IsOnStack(auxSlots[i])); } } + if (deepCopy && instance->HasObjectArray()) + { + // Assert that this block of code will not overwrite inline slot data + Assert(!typeHandler->IsObjectHeaderInlinedTypeHandler()); + + // While the objectArray can be any array type, a DynamicObject that is created on the + // stack will only have one of these three types (as these are also the only array types + // that can be allocated on the stack). + Assert(Js::JavascriptArray::Is(instance->GetObjectArrayOrFlagsAsArray()) + || Js::JavascriptNativeIntArray::Is(instance->GetObjectArrayOrFlagsAsArray()) + || Js::JavascriptNativeFloatArray::Is(instance->GetObjectArrayOrFlagsAsArray()) + ); + + // Since a deep copy was requested for this DynamicObject, deep copy the object array as well + SetObjectArray(JavascriptArray::DeepCopyInstance(instance->GetObjectArrayOrFlagsAsArray())); + } + else + { + // Otherwise, assert that there is either + // - no object array to deep copy + // - an object array, but no deep copy needed + // - data in the objectArray member, but it is inline slot data + // - data in the objectArray member, but it is array flags + Assert( + (instance->GetObjectArrayOrFlagsAsArray() == nullptr) || + (!deepCopy || typeHandler->IsObjectHeaderInlinedTypeHandler() || instance->UsesObjectArrayOrFlagsAsFlags()) + ); + } + #if ENABLE_OBJECT_SOURCE_TRACKING TTD::InitializeDiagnosticOriginInformation(this->TTDDiagOriginInfo); #endif @@ -805,7 +855,7 @@ namespace Js } DynamicObject * - DynamicObject::BoxStackInstance(DynamicObject * instance) + DynamicObject::BoxStackInstance(DynamicObject * instance, bool deepCopy) { Assert(ThreadContext::IsOnStack(instance)); // On the stack, the we reserved a pointer before the object as to store the boxed value @@ -819,11 +869,11 @@ namespace Js size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize(); if (inlineSlotsSize) { - boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, DynamicObject, instance); + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, DynamicObject, instance, deepCopy); } else { - boxedInstance = RecyclerNew(instance->GetRecycler(), DynamicObject, instance); + boxedInstance = RecyclerNew(instance->GetRecycler(), DynamicObject, instance, deepCopy); } *boxedInstanceRef = boxedInstance; diff --git a/lib/Runtime/Types/DynamicObject.h b/lib/Runtime/Types/DynamicObject.h index 15517c861d8..dec5c725e56 100644 --- a/lib/Runtime/Types/DynamicObject.h +++ b/lib/Runtime/Types/DynamicObject.h @@ -84,10 +84,26 @@ namespace Js #endif private: + // Memory layout of DynamicObject can be one of the following: + // (#1) (#2) (#3) + // +--------------+ +--------------+ +--------------+ + // | vtable, etc. | | vtable, etc. | | vtable, etc. | + // |--------------| |--------------| |--------------| + // | auxSlots | | auxSlots | | inline slots | + // | union | | union | | | + // +--------------+ |--------------| | | + // | inline slots | | | + // +--------------+ +--------------+ + // The allocation size of inline slots is variable and dependent on profile data for the + // object. The offset of the inline slots is managed by DynamicTypeHandler. + // More details for the layout scenarios below. + Field(Field(Var)*) auxSlots; - // The objectArrayOrFlags field can store one of two things: - // a) a pointer to the object array holding numeric properties of this object, or - // b) a bitfield of flags. + + // The objectArrayOrFlags field can store one of three things: + // a) a pointer to the object array holding numeric properties of this object (#1, #2), or + // b) a bitfield of flags (#1, #2), or + // c) inline slot data (#3) // Because object arrays are not commonly used, the storage space can be reused to carry information that // can improve performance for typical objects. To indicate the bitfield usage we set the least significant bit to 1. // Object array pointer always trumps the flags, such that when the first numeric property is added to an @@ -95,10 +111,13 @@ namespace Js // For functional correctness, some other fallback mechanism must exist to convey the information contained in flags. // This fields always starts off initialized to null. Currently, only JavascriptArray overrides it to store flags, the // bits it uses are DynamicObjectFlags::AllArrayFlags. + // Regarding c) above, inline slots can be stored within the allocation of sizeof(DynamicObject) (#3) or after + // sizeof(DynamicObject) (#2). This is indicated by GetTypeHandler()->IsObjectHeaderInlinedTypeHandler(); when true, the + // inline slots are within the object, and thus the union members *and* auxSlots actually contain inline slot data. union { - Field(ArrayObject *) objectArray; // Only if !IsAnyArray + Field(ArrayObject *) objectArray; // Only if !IsAnyArray struct // Only if IsAnyArray { Field(DynamicObjectFlags) arrayFlags; @@ -122,7 +141,7 @@ namespace Js DynamicObject(DynamicType * type, ScriptContext * scriptContext); // For boxing stack instance - DynamicObject(DynamicObject * instance); + DynamicObject(DynamicObject * instance, bool deepCopy); uint16 GetOffsetOfInlineSlots() const; @@ -317,7 +336,7 @@ namespace Js ProfileId GetArrayCallSiteIndex() const; void SetArrayCallSiteIndex(ProfileId profileId); - static DynamicObject * BoxStackInstance(DynamicObject * instance); + static DynamicObject * BoxStackInstance(DynamicObject * instance, bool deepCopy); private: ArrayObject* EnsureObjectArray(); From 26371402f106e81797122f5b4bbd9f5e54bd2553 Mon Sep 17 00:00:00 2001 From: Paul Leathers Date: Wed, 4 Apr 2018 12:26:06 -0700 Subject: [PATCH 5/8] [CVE-2018-0979] Incorrect byte code can cause dereference of uninitialized stack location - Internal --- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 140 +++++++++--------- lib/Runtime/ByteCode/ByteCodeGenerator.cpp | 27 ++-- lib/Runtime/ByteCode/ByteCodeGenerator.h | 4 +- lib/Runtime/ByteCode/ScopeInfo.cpp | 14 +- lib/Runtime/ByteCode/ScopeInfo.h | 5 +- lib/Runtime/ByteCode/Symbol.cpp | 12 +- lib/Runtime/ByteCode/Symbol.h | 6 +- lib/Runtime/Language/AsmJsModule.cpp | 2 +- .../Language/InterpreterStackFrame.cpp | 18 ++- 9 files changed, 121 insertions(+), 107 deletions(-) diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index 423760614a9..bf939681041 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -397,7 +397,7 @@ void ByteCodeGenerator::TrackFunctionDeclarationPropertyForDebugger(Symbol *func // Note: we don't have to check symbol->GetIsTrackedForDebugger, as we are not doing actual work here, // which is done in other Track* functions that we call. - if (functionDeclarationSymbol->IsInSlot(funcInfoParent)) + if (functionDeclarationSymbol->IsInSlot(this, funcInfoParent)) { if (functionDeclarationSymbol->GetScope()->GetIsObject()) { @@ -412,7 +412,7 @@ void ByteCodeGenerator::TrackFunctionDeclarationPropertyForDebugger(Symbol *func // Make sure the property has a slot. This will bump up the size of the slot array if necessary. // Note that slot array inner function bindings are tracked even in non-debug mode in order // to keep the lifetime of the closure binding that could escape around for heap enumeration. - functionDeclarationSymbol->EnsureScopeSlot(funcInfoParent); + functionDeclarationSymbol->EnsureScopeSlot(this, funcInfoParent); functionDeclarationSymbol->EnsurePosition(this); this->TrackSlotArrayPropertyForDebugger( this->Writer()->GetCurrentDebuggerScope(), @@ -634,7 +634,7 @@ void ByteCodeGenerator::InitBlockScopedContent(ParseNode *pnodeBlock, Js::Debugg this->m_writer.ElementRootU(op, funcInfo->FindOrAddReferencedPropertyId(propertyId)); } } - else if (sym->IsInSlot(funcInfo) || (scope->GetIsObject() && sym->NeedsSlotAlloc(funcInfo))) + else if (sym->IsInSlot(this, funcInfo) || (scope->GetIsObject() && sym->NeedsSlotAlloc(this, funcInfo))) { if (scope->GetIsObject()) { @@ -684,7 +684,7 @@ void ByteCodeGenerator::InitBlockScopedContent(ParseNode *pnodeBlock, Js::Debugg } // Syms that begin in register may be delay-captured. In debugger mode, such syms // will live only in slots, so tell the debugger to find them there. - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { TrackSlotArrayPropertyForDebugger(debuggerScope, sym, sym->EnsurePosition(this), pnode->nop == knopConstDecl ? Js::DebuggerScopePropertyFlags_Const : Js::DebuggerScopePropertyFlags_None); } @@ -1088,7 +1088,7 @@ void ByteCodeGenerator::DefineCachedFunctions(FuncInfo *funcInfoParent) { this->DefineOneFunction(pnodeFnc, funcInfoParent); } - else if (!sym->IsInSlot(funcInfoParent) && sym->GetLocation() != Js::Constants::NoRegister) + else if (!sym->IsInSlot(this, funcInfoParent) && sym->GetLocation() != Js::Constants::NoRegister) { // If it was defined by InitCachedFuncs, do we need to put it in a register rather than a slot? m_writer.Reg1Unsigned1(Js::OpCode::GetCachedFunc, sym->GetLocation(), slotCount); @@ -1159,7 +1159,7 @@ void EmitAssignmentToFuncName(ParseNode *pnodeFnc, ByteCodeGenerator *byteCodeGe } else { - if (sym->NeedsSlotAlloc(funcInfoParent)) + if (sym->NeedsSlotAlloc(byteCodeGenerator, funcInfoParent)) { if (!sym->GetHasNonCommittedReference() || (funcInfoParent->GetParsedFunctionBody()->DoStackNestedFunc())) @@ -1278,7 +1278,7 @@ Js::RegSlot ByteCodeGenerator::DefineOneFunction(ParseNode *pnodeFnc, FuncInfo * // be sure to track the register location as well. && !(funcInfoParent->IsGlobalFunction() && !isFunctionDeclarationInBlock)) { - if (!funcSymbol->IsInSlot(funcInfoParent)) + if (!funcSymbol->IsInSlot(this, funcInfoParent)) { funcInfoParent->byteCodeFunction->GetFunctionBody()->InsertSymbolToRegSlotList(funcSymbol->GetName(), pnodeFnc->location, funcInfoParent->varRegsCount); } @@ -1337,7 +1337,7 @@ void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo) { EmitPropStoreForSpecialSymbol(sym->GetLocation(), sym, sym->GetPid(), funcInfo, true); - if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(funcInfo)) + if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(this, funcInfo)) { byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), sym->GetLocation(), funcInfo->varRegsCount); } @@ -1401,7 +1401,7 @@ void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo) } else if (!sym->IsArguments()) { - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { if (!sym->GetHasNonCommittedReference() || (sym->GetHasFuncAssignment() && funcInfo->GetParsedFunctionBody()->DoStackNestedFunc())) @@ -1414,19 +1414,19 @@ void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo) // Undef-initialize the home location if it is a register (not closure-captured, or else capture // is delayed) or a property of an object. - if ((!sym->GetHasInit() && !sym->IsInSlot(funcInfo)) || + if ((!sym->GetHasInit() && !sym->IsInSlot(this, funcInfo)) || (funcInfo->bodyScope->GetIsObject() && !funcInfo->GetHasCachedScope())) { Js::RegSlot reg = sym->GetLocation(); if (reg == Js::Constants::NoRegister) { - Assert(sym->IsInSlot(funcInfo)); + Assert(sym->IsInSlot(this, funcInfo)); reg = funcInfo->AcquireTmpRegister(); } this->m_writer.Reg1(Js::OpCode::LdUndef, reg); this->EmitLocalPropInit(reg, sym, funcInfo); - if (ShouldTrackDebuggerMetadata() && !sym->GetHasInit() && !sym->IsInSlot(funcInfo)) + if (ShouldTrackDebuggerMetadata() && !sym->GetHasInit() && !sym->IsInSlot(this, funcInfo)) { byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), reg, funcInfo->varRegsCount); } @@ -1436,7 +1436,7 @@ void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo) } else if (ShouldTrackDebuggerMetadata()) { - if (!sym->GetHasInit() && !sym->IsInSlot(funcInfo)) + if (!sym->GetHasInit() && !sym->IsInSlot(this, funcInfo)) { Js::RegSlot reg = sym->GetLocation(); if (reg != Js::Constants::NoRegister) @@ -1509,7 +1509,7 @@ void ByteCodeGenerator::InitBlockScopedNonTemps(ParseNode *pnode, FuncInfo *func auto fnInit = [this, funcInfo](ParseNode *pnode) { Symbol *sym = pnode->sxVar.sym; - if (!sym->IsInSlot(funcInfo) && !sym->GetIsGlobal() && !sym->GetIsModuleImport()) + if (!sym->IsInSlot(this, funcInfo) && !sym->GetIsGlobal() && !sym->GetIsModuleImport()) { this->m_writer.Reg1(Js::OpCode::InitUndecl, pnode->sxVar.sym->GetLocation()); } @@ -1609,7 +1609,7 @@ void ByteCodeGenerator::EmitScopeObjectInit(FuncInfo *funcInfo) MapFormalsWithoutRest(pnodeFnc, initArg); // If the rest is in the slot - we need to keep that slot. - if (pnodeFnc->sxFnc.pnodeRest != nullptr && pnodeFnc->sxFnc.pnodeRest->sxVar.sym->IsInSlot(funcInfo)) + if (pnodeFnc->sxFnc.pnodeRest != nullptr && pnodeFnc->sxFnc.pnodeRest->sxVar.sym->IsInSlot(this, funcInfo)) { Symbol::SaveToPropIdArray(pnodeFnc->sxFnc.pnodeRest->sxVar.sym, propIds, this); } @@ -1820,9 +1820,9 @@ void ByteCodeGenerator::InitScopeSlotArray(FuncInfo * funcInfo) propertyIdsForScopeSlotArray[slot] = propId; }; - auto setPropIdsForScopeSlotArray = [funcInfo, setPropertyIdForScopeSlotArray](Symbol *const sym) + auto setPropIdsForScopeSlotArray = [this, funcInfo, setPropertyIdForScopeSlotArray](Symbol *const sym) { - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { // All properties should get correct propertyId here. Assert(sym->HasScopeSlot()); // We can't allocate scope slot now. Any symbol needing scope slot must have allocated it before this point. @@ -1882,7 +1882,7 @@ void ByteCodeGenerator::LoadAllConstants(FuncInfo *funcInfo) uint count = 0; funcInfo->GetBodyScope()->ForEachSymbol([&](Symbol *const sym) { - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { // All properties should get correct propertyId here. count++; @@ -1893,7 +1893,7 @@ void ByteCodeGenerator::LoadAllConstants(FuncInfo *funcInfo) { funcInfo->GetParamScope()->ForEachSymbol([&](Symbol *const sym) { - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { // All properties should get correct propertyId here. count++; @@ -2063,7 +2063,7 @@ void ByteCodeGenerator::LoadAllConstants(FuncInfo *funcInfo) Js::RegSlot ldFuncExprDst = sym->GetLocation(); this->m_writer.Reg1(Js::OpCode::LdFuncExpr, ldFuncExprDst); - if (sym->IsInSlot(funcInfo)) + if (sym->IsInSlot(this, funcInfo)) { Js::RegSlot scopeLocation; AnalysisAssert(funcInfo->funcExprScope); @@ -2360,12 +2360,12 @@ void ByteCodeGenerator::HomeArguments(FuncInfo *funcInfo) if (ShouldTrackDebuggerMetadata()) { // Add formals to the debugger propertyidcontainer for reg slots - auto addFormalsToPropertyIdContainer = [funcInfo](ParseNode *pnodeFormal) + auto addFormalsToPropertyIdContainer = [this, funcInfo](ParseNode *pnodeFormal) { if (pnodeFormal->IsVarLetOrConst()) { Symbol* formal = pnodeFormal->sxVar.sym; - if (!formal->IsInSlot(funcInfo)) + if (!formal->IsInSlot(this, funcInfo)) { Assert(!formal->GetHasInit()); funcInfo->GetParsedFunctionBody()->InsertSymbolToRegSlotList(formal->GetName(), formal->GetLocation(), funcInfo->varRegsCount); @@ -2625,7 +2625,7 @@ void ByteCodeGenerator::EmitDefaultArgs(FuncInfo *funcInfo, ParseNode *pnode) Emit(pnodeArg->sxVar.pnodeInit, this, funcInfo, false); pnodeArg->sxVar.sym->SetNeedDeclaration(false); // After emit to prevent foo(a = a) - if (funcInfo->GetHasArguments() && pnodeArg->sxVar.sym->IsInSlot(funcInfo)) + if (funcInfo->GetHasArguments() && pnodeArg->sxVar.sym->IsInSlot(this, funcInfo)) { EmitPropStore(pnodeArg->sxVar.pnodeInit->location, pnodeArg->sxVar.sym, pnodeArg->sxVar.pid, funcInfo, true); @@ -2640,7 +2640,7 @@ void ByteCodeGenerator::EmitDefaultArgs(FuncInfo *funcInfo, ParseNode *pnode) m_writer.MarkLabel(noDefaultLabel); - if (funcInfo->GetHasArguments() && pnodeArg->sxVar.sym->IsInSlot(funcInfo)) + if (funcInfo->GetHasArguments() && pnodeArg->sxVar.sym->IsInSlot(this, funcInfo)) { EmitPropStore(location, pnodeArg->sxVar.sym, pnodeArg->sxVar.pid, funcInfo, true); @@ -2936,7 +2936,7 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode) if (sym != nullptr && sym->IsSpecialSymbol()) { EmitPropStoreForSpecialSymbol(sym->GetLocation(), sym, sym->GetPid(), funcInfo, true); - if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(funcInfo)) + if (ShouldTrackDebuggerMetadata() && !sym->IsInSlot(this, funcInfo)) { byteCodeFunction->InsertSymbolToRegSlotList(sym->GetName(), sym->GetLocation(), funcInfo->varRegsCount); } @@ -3049,16 +3049,16 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode) Symbol* varSym = funcInfo->GetBodyScope()->FindLocalSymbol(param->GetName()); if ((funcSym == nullptr || funcSym != param) // Do not copy the symbol over to body as the function expression symbol // is expected to stay inside the function expression scope - && (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister))) + && (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(this, funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister))) { if (!varSym->GetNeedDeclaration()) { - if (param->IsInSlot(funcInfo)) + if (param->IsInSlot(this, funcInfo)) { // Simulating EmitPropLoad here. We can't directly call the method as we have to use the param scope specifically. // Walking the scope chain is not possible at this time. Js::RegSlot tempReg = funcInfo->AcquireTmpRegister(); - Js::PropertyId slot = param->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = param->EnsureScopeSlot(this, funcInfo); Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(paramScope, slot); Js::OpCode op = paramScope->GetIsObject() ? Js::OpCode::LdParamObjSlot : Js::OpCode::LdParamSlot; slot = slot + (paramScope->GetIsObject() ? 0 : Js::ScopeSlots::FirstSlotIndex); @@ -3442,7 +3442,7 @@ void ByteCodeGenerator::EmitScopeList(ParseNode *pnode, ParseNode *breakOnBodySc } } -void EnsureFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) +void ByteCodeGenerator::EnsureFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) { if (pnodeFnc->sxFnc.pnodeName) { @@ -3452,21 +3452,21 @@ void EnsureFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) // We will allocate scope slot for the arguments symbol during EmitLocalPropInit. if (sym && !sym->IsArguments()) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } } // Similar to EnsureFncScopeSlot visitor function, but verifies that a slot is needed before assigning it. -void CheckFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) +void ByteCodeGenerator::CheckFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo) { if (pnodeFnc->sxFnc.pnodeName && pnodeFnc->sxFnc.pnodeName->nop == knopVarDecl) { Assert(pnodeFnc->sxFnc.pnodeName->nop == knopVarDecl); Symbol *sym = pnodeFnc->sxFnc.pnodeName->sxVar.sym; - if (sym && sym->NeedsSlotAlloc(funcInfo)) + if (sym && sym->NeedsSlotAlloc(this, funcInfo)) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } } @@ -3517,7 +3517,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) { funcInfo->paramScope->AddSymbol(sym); } - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); if (sym->GetHasNonLocalReference()) { sym->GetScope()->SetHasOwnLocalInClosure(true); @@ -3602,26 +3602,26 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) { if (pnode->IsVarLetOrConst()) { - pnode->sxVar.sym->EnsureScopeSlot(funcInfo); + pnode->sxVar.sym->EnsureScopeSlot(this, funcInfo); } }); - MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->sxVar.sym->EnsureScopeSlot(funcInfo); }); + MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->sxVar.sym->EnsureScopeSlot(this, funcInfo); }); // Only allocate scope slot for "arguments" when really necessary. "hasDeferredChild" // doesn't require scope slot for "arguments" because inner functions can't access // outer function's arguments directly. sym = funcInfo->GetArgumentsSymbol(); Assert(sym); - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } sym = funcInfo->root->sxFnc.GetFuncSymbol(); - if (sym && sym->NeedsSlotAlloc(funcInfo)) + if (sym && sym->NeedsSlotAlloc(this, funcInfo)) { if (funcInfo->funcExprScope && funcInfo->funcExprScope->GetIsObject()) { @@ -3629,7 +3629,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) } else if (funcInfo->GetFuncExprNameReference()) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } @@ -3646,13 +3646,13 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) // The position should match the location; otherwise, it has been shadowed by parameter with the same name. if (formal->GetLocation() + 1 == pos) { - pnode->sxVar.sym->EnsureScopeSlot(funcInfo); + pnode->sxVar.sym->EnsureScopeSlot(this, funcInfo); } } pos++; }; MapFormals(pnodeFnc, moveArgToReg); - MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->sxVar.sym->EnsureScopeSlot(funcInfo); }); + MapFormalsFromPattern(pnodeFnc, [&](ParseNode *pnode) { pnode->sxVar.sym->EnsureScopeSlot(this, funcInfo); }); } for (pnode = pnodeFnc->sxFnc.pnodeVars; pnode; pnode = pnode->sxVar.pnodeNext) @@ -3666,7 +3666,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) } if (sym->GetSymbolType() == STVariable && !sym->IsArguments()) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } } @@ -3719,9 +3719,9 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) { sym = funcInfo->bodyScope->FindLocalSymbol(sym->GetName()); } - if (sym->GetSymbolType() == STVariable && sym->NeedsSlotAlloc(funcInfo) && !sym->IsArguments()) + if (sym->GetSymbolType() == STVariable && sym->NeedsSlotAlloc(this, funcInfo) && !sym->IsArguments()) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } } @@ -3731,9 +3731,9 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) if (pnode->IsVarLetOrConst()) { sym = pnode->sxVar.sym; - if (sym->GetSymbolType() == STFormal && sym->NeedsSlotAlloc(funcInfo)) + if (sym->GetSymbolType() == STFormal && sym->NeedsSlotAlloc(this, funcInfo)) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } }; @@ -3748,7 +3748,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc) // There is no eval so the arguments may be captured in a lambda. // But we cannot relay on slots getting allocated while the lambda is emitted as the function body may be reparsed. - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } if (pnodeFnc->sxFnc.pnodeBody) @@ -3889,9 +3889,9 @@ void ByteCodeGenerator::EnsureLetConstScopeSlots(ParseNode *pnodeBlock, FuncInfo auto ensureLetConstSlots = ([this, funcInfo, callsEval](ParseNode *pnode) { Symbol *sym = pnode->sxVar.sym; - if (callsEval || sym->NeedsSlotAlloc(funcInfo)) + if (callsEval || sym->NeedsSlotAlloc(this, funcInfo)) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); this->ProcessCapturedSym(sym); } }); @@ -3986,9 +3986,9 @@ void ByteCodeGenerator::StartEmitCatch(ParseNode *pnodeCatch) } Assert(sym->GetScopeSlot() == Js::Constants::NoProperty); - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } }); @@ -4010,12 +4010,12 @@ void ByteCodeGenerator::StartEmitCatch(ParseNode *pnodeCatch) if (scope->GetMustInstantiate()) { - if (sym->IsInSlot(funcInfo)) + if (sym->IsInSlot(this, funcInfo)) { // Since there is only one symbol we are pushing to slot. // Also in order to make IsInSlot to return true - forcing the sym-has-non-local-reference. this->ProcessCapturedSym(sym); - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); } } @@ -4394,10 +4394,10 @@ void ByteCodeGenerator::EmitLocalPropInit(Js::RegSlot rhsLocation, Symbol *sym, Scope *scope = sym->GetScope(); // Check consistency of sym->IsInSlot. - Assert(sym->NeedsSlotAlloc(funcInfo) || sym->GetScopeSlot() == Js::Constants::NoProperty); + Assert(sym->NeedsSlotAlloc(this, funcInfo) || sym->GetScopeSlot() == Js::Constants::NoProperty); // Arrived at the scope in which the property was defined. - if (sym->NeedsSlotAlloc(funcInfo)) + if (sym->NeedsSlotAlloc(this, funcInfo)) { // The property is in memory rather than register. We'll have to load it from the slots. if (scope->GetIsObject()) @@ -4431,7 +4431,7 @@ void ByteCodeGenerator::EmitLocalPropInit(Js::RegSlot rhsLocation, Symbol *sym, else { // Make sure the property has a slot. This will bump up the size of the slot array if necessary. - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); Js::RegSlot slotReg = scope->GetCanMerge() ? funcInfo->frameSlotsRegister : scope->GetLocation(); // Now store the property to its slot. Js::OpCode op = this->GetStSlotOp(scope, -1, slotReg, false, funcInfo); @@ -4728,7 +4728,7 @@ void ByteCodeGenerator::EmitPropStore(Js::RegSlot rhsLocation, Symbol *sym, Iden this->m_writer.W1(Js::OpCode::RuntimeTypeError, SCODE_CODE(JSERR_CantAssignToReadOnly)); } } - else if (sym->IsInSlot(funcInfo) || envIndex != -1) + else if (sym->IsInSlot(this, funcInfo) || envIndex != -1) { if (!isConstDecl && sym->GetIsConst()) { @@ -4738,7 +4738,7 @@ void ByteCodeGenerator::EmitPropStore(Js::RegSlot rhsLocation, Symbol *sym, Iden Js::OpCode::RuntimeTypeError, SCODE_CODE(ERRAssignmentToConst)); } // Make sure the property has a slot. This will bump up the size of the slot array if necessary. - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); bool chkBlockVar = !isLetDecl && !isConstDecl && NeedCheckBlockVar(sym, scope, funcInfo); // The property is in memory rather than register. We'll have to load it from the slots. @@ -5042,9 +5042,9 @@ void ByteCodeGenerator::EmitPropLoad(Js::RegSlot lhsLocation, Symbol *sym, Ident if (sym && sym->GetNeedDeclaration() && scope->GetFunc() == funcInfo && !skipUseBeforeDeclarationCheck) { // Ensure this symbol has a slot if it needs one. - if (sym->IsInSlot(funcInfo)) + if (sym->IsInSlot(this, funcInfo)) { - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); funcInfo->FindOrAddSlotProfileId(scope, slot); } @@ -5124,10 +5124,10 @@ void ByteCodeGenerator::EmitPropLoad(Js::RegSlot lhsLocation, Symbol *sym, Ident } } } - else if (sym->IsInSlot(funcInfo) || envIndex != -1) + else if (sym->IsInSlot(this, funcInfo) || envIndex != -1) { // Make sure the property has a slot. This will bump up the size of the slot array if necessary. - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(scope, slot); bool chkBlockVar = NeedCheckBlockVar(sym, scope, funcInfo); Js::OpCode op; @@ -5310,7 +5310,7 @@ void ByteCodeGenerator::EmitPropDelete(Js::RegSlot lhsLocation, Symbol *sym, Ide else { // The delete will look like a non-local reference, so make sure a slot is reserved. - sym->EnsureScopeSlot(funcInfo); + sym->EnsureScopeSlot(this, funcInfo); this->m_writer.Reg1(Js::OpCode::LdFalse, lhsLocation); } @@ -5462,9 +5462,9 @@ void ByteCodeGenerator::EmitPropTypeof(Js::RegSlot lhsLocation, Symbol *sym, Ide if (sym && sym->GetNeedDeclaration() && scope->GetFunc() == funcInfo) { // Ensure this symbol has a slot if it needs one. - if (sym->IsInSlot(funcInfo)) + if (sym->IsInSlot(this, funcInfo)) { - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); funcInfo->FindOrAddSlotProfileId(scope, slot); } @@ -5493,10 +5493,10 @@ void ByteCodeGenerator::EmitPropTypeof(Js::RegSlot lhsLocation, Symbol *sym, Ide this->EmitTypeOfFld(funcInfo, propertyId, lhsLocation, ByteCodeGenerator::RootObjectRegister, Js::OpCode::LdRootFldForTypeOf); } } - else if (sym->IsInSlot(funcInfo) || envIndex != -1) + else if (sym->IsInSlot(this, funcInfo) || envIndex != -1) { // Make sure the property has a slot. This will bump up the size of the slot array if necessary. - Js::PropertyId slot = sym->EnsureScopeSlot(funcInfo); + Js::PropertyId slot = sym->EnsureScopeSlot(this, funcInfo); Js::ProfileId profileId = funcInfo->FindOrAddSlotProfileId(scope, slot); Js::RegSlot tmpLocation = funcInfo->AcquireTmpRegister(); bool chkBlockVar = NeedCheckBlockVar(sym, scope, funcInfo); @@ -5877,7 +5877,7 @@ void EmitReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncI { funcInfo->AcquireLoc(pnode->sxCall.pnodeTarget); } - if (sym && (sym->IsInSlot(funcInfo) || sym->GetScope()->GetFunc() != funcInfo)) + if (sym && (sym->IsInSlot(byteCodeGenerator, funcInfo) || sym->GetScope()->GetFunc() != funcInfo)) { // Can't get the value from the assigned register, so load it here. EmitLoad(pnode->sxCall.pnodeTarget, byteCodeGenerator, funcInfo); @@ -11368,7 +11368,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func auto ParamTrackAndInitialization = [&](Symbol *sym, bool initializeParam, Js::RegSlot location) { - if (sym->IsInSlot(funcInfo)) + if (sym->IsInSlot(byteCodeGenerator, funcInfo)) { Assert(scope->GetMustInstantiate()); if (scope->GetIsObject()) diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index f12c3267412..931d337e07d 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -2545,7 +2545,7 @@ FuncInfo* PreVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerato } PreVisitBlock(pnode->sxFnc.pnodeScopes, byteCodeGenerator); // If we have arguments, we are going to need locations if the function is in strict mode or we have a non-simple parameter list. This is because we will not create a scope object. - bool assignLocationForFormals = !ByteCodeGenerator::NeedScopeObjectForArguments(funcInfo, funcInfo->root); + bool assignLocationForFormals = !byteCodeGenerator->NeedScopeObjectForArguments(funcInfo, funcInfo->root); AddArgsToScope(pnode, byteCodeGenerator, assignLocationForFormals); return funcInfo; @@ -2605,7 +2605,7 @@ void AssignFuncSymRegister(ParseNode * pnode, ByteCodeGenerator * byteCodeGenera Symbol * functionScopeVarSym = sym->GetFuncScopeVarSym(); if (functionScopeVarSym && !functionScopeVarSym->GetIsGlobal() && - !functionScopeVarSym->IsInSlot(sym->GetScope()->GetFunc())) + !functionScopeVarSym->IsInSlot(byteCodeGenerator, sym->GetScope()->GetFunc())) { byteCodeGenerator->AssignRegister(functionScopeVarSym); } @@ -2780,7 +2780,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat { if (top->GetCallsEval() || top->GetChildCallsEval() || - (top->GetHasArguments() && ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode)) || + (top->GetHasArguments() && byteCodeGenerator->NeedScopeObjectForArguments(top, pnode)) || top->GetHasLocalInClosure() || (top->funcExprScope && top->funcExprScope->GetMustInstantiate()) || // When we have split scope normally either eval will be present or the GetHasLocalInClosure will be true as one of the formal is @@ -2849,10 +2849,10 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat }; // We need to include the rest as well -as it will get slot assigned. - if (ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode)) + if (byteCodeGenerator->NeedScopeObjectForArguments(top, pnode)) { MapFormals(pnode, setArgScopeSlot); - if (argSym->NeedsSlotAlloc(top)) + if (argSym->NeedsSlotAlloc(byteCodeGenerator, top)) { Assert(argSym->GetScopeSlot() == Js::Constants::NoProperty); argSym->SetScopeSlot(i++); @@ -2863,7 +2863,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat top->paramScope->SetScopeSlotCount(i); Assert(top->GetHasHeapArguments()); - if (ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode) + if (byteCodeGenerator->NeedScopeObjectForArguments(top, pnode) && !pnode->sxFnc.HasNonSimpleParameterList()) { top->byteCodeFunction->SetHasImplicitArgIns(false); @@ -3068,7 +3068,7 @@ void ByteCodeGenerator::ProcessCapturedSym(Symbol *sym) FuncInfo *funcHome = sym->GetScope()->GetFunc(); FuncInfo *funcChild = funcHome->GetCurrentChildFunction(); - Assert(sym->NeedsSlotAlloc(funcHome) || sym->GetIsGlobal() || sym->GetIsModuleImport() || sym->GetIsModuleExportStorage()); + Assert(sym->NeedsSlotAlloc(this, funcHome) || sym->GetIsGlobal() || sym->GetIsModuleImport() || sym->GetIsModuleExportStorage()); // If this is not a local property, or not all its references can be tracked, or // it's not scoped to the function, or we're in debug mode, disable the delayed capture optimization. @@ -4953,11 +4953,11 @@ void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) } // Don't give the declared var a register if it's in a closure, because the closure slot // is its true "home". (Need to check IsGlobal again as the sym may have changed above.) - if (!sym->GetIsGlobal() && !sym->IsInSlot(funcInfo)) + if (!sym->GetIsGlobal() && !sym->IsInSlot(byteCodeGenerator, funcInfo)) { if (PHASE_TRACE(Js::DelayCapturePhase, funcInfo->byteCodeFunction)) { - if (sym->NeedsSlotAlloc(byteCodeGenerator->TopFuncInfo())) + if (sym->NeedsSlotAlloc(byteCodeGenerator, byteCodeGenerator->TopFuncInfo())) { Output::Print(_u("--- DelayCapture: Delayed capturing symbol '%s' during initialization.\n"), sym->GetName().GetBuffer()); @@ -5011,12 +5011,12 @@ void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) if (!sym->GetIsGlobal() && !sym->GetIsMember() && byteCodeGenerator->TopFuncInfo() == sym->GetScope()->GetEnclosingFunc() && - !sym->IsInSlot(byteCodeGenerator->TopFuncInfo()) && + !sym->IsInSlot(byteCodeGenerator, byteCodeGenerator->TopFuncInfo()) && !sym->HasVisitedCapturingFunc()) { if (PHASE_TRACE(Js::DelayCapturePhase, byteCodeGenerator->TopFuncInfo()->byteCodeFunction)) { - if (sym->NeedsSlotAlloc(byteCodeGenerator->TopFuncInfo())) + if (sym->NeedsSlotAlloc(byteCodeGenerator, byteCodeGenerator->TopFuncInfo())) { Output::Print(_u("--- DelayCapture: Delayed capturing symbol '%s'.\n"), sym->GetName().GetBuffer()); @@ -5177,8 +5177,7 @@ Js::FunctionBody * ByteCodeGenerator::MakeGlobalFunctionBody(ParseNode *pnode) return func; } -/* static */ -bool ByteCodeGenerator::NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc) +bool ByteCodeGenerator::NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc) const { // We can avoid creating a scope object with arguments present if: bool dontNeedScopeObject = @@ -5187,6 +5186,8 @@ bool ByteCodeGenerator::NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNod // Either we are in strict mode, or have strict mode formal semantics from a non-simple parameter list, and && (funcInfo->GetIsStrictMode() || pnodeFnc->sxFnc.HasNonSimpleParameterList()) + // We're not in eval or event handler, which will force the scope(s) to be objects + && !(this->flags & (fscrEval | fscrImplicitThis | fscrImplicitParents)) // Neither of the scopes are objects && !funcInfo->paramScope->GetIsObject() && !funcInfo->bodyScope->GetIsObject(); diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.h b/lib/Runtime/ByteCode/ByteCodeGenerator.h index a27096ae8d5..99a50f8f1b5 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.h +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.h @@ -398,10 +398,12 @@ class ByteCodeGenerator Js::FunctionBody *EnsureFakeGlobalFuncForUndefer(ParseNode *pnode); Js::FunctionBody *MakeGlobalFunctionBody(ParseNode *pnode); - static bool NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc); + bool NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc) const; void AddFuncInfoToFinalizationSet(FuncInfo *funcInfo); void FinalizeFuncInfos(); + void CheckFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo); + void EnsureFncDeclScopeSlot(ParseNode *pnodeFnc, FuncInfo *funcInfo); Js::OpCode GetStSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, bool chkBlockVar, FuncInfo *funcInfo); Js::OpCode GetLdSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, FuncInfo *funcInfo); diff --git a/lib/Runtime/ByteCode/ScopeInfo.cpp b/lib/Runtime/ByteCode/ScopeInfo.cpp index 870974b0775..9f03d8dfbe8 100644 --- a/lib/Runtime/ByteCode/ScopeInfo.cpp +++ b/lib/Runtime/ByteCode/ScopeInfo.cpp @@ -22,7 +22,7 @@ namespace Js else if (needScopeSlot) { // Any symbol may have non-local ref from deferred child. Allocate slot for it. - scopeSlot = sym->EnsureScopeSlot(mapSymbolData->func); + scopeSlot = sym->EnsureScopeSlot(mapSymbolData->byteCodeGenerator, mapSymbolData->func); } if (needScopeSlot || sym->GetIsModuleExportStorage()) @@ -52,7 +52,7 @@ namespace Js // // Create scope info for a single scope. // - ScopeInfo* ScopeInfo::SaveOneScopeInfo(/*ByteCodeGenerator* byteCodeGenerator, ParseableFunctionInfo* parent,*/ Scope* scope, ScriptContext *scriptContext) + ScopeInfo* ScopeInfo::SaveOneScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope* scope, ScriptContext *scriptContext) { Assert(scope->GetScopeInfo() == nullptr); Assert(scope->GetScopeType() != ScopeType_Global); @@ -82,7 +82,7 @@ namespace Js scope->GetFunc()->name, count, scopeInfo->isObject ? _u("isObject") : _u("")); - MapSymbolData mapSymbolData = { scope->GetFunc(), 0 }; + MapSymbolData mapSymbolData = { byteCodeGenerator, scope->GetFunc(), 0 }; scope->ForEachSymbol([&mapSymbolData, scopeInfo, scope](Symbol * sym) { Assert(scope == sym->GetScope()); @@ -109,7 +109,7 @@ namespace Js // // Save scope info for an individual scope and link it to its enclosing scope. // - ScopeInfo * ScopeInfo::SaveScopeInfo(Scope * scope/*ByteCodeGenerator* byteCodeGenerator, FuncInfo* parentFunc, FuncInfo* func*/, ScriptContext * scriptContext) + ScopeInfo * ScopeInfo::SaveScopeInfo(ByteCodeGenerator* byteCodeGenerator, Scope * scope, ScriptContext * scriptContext) { // Advance past scopes that will be excluded from the closure environment. (But note that we always want the body scope.) while (scope && (!scope->GetMustInstantiate() && scope != scope->GetFunc()->GetBodyScope())) @@ -131,13 +131,13 @@ namespace Js } // Do the work for this scope. - scopeInfo = ScopeInfo::SaveOneScopeInfo(scope, scriptContext); + scopeInfo = ScopeInfo::SaveOneScopeInfo(byteCodeGenerator, scope, scriptContext); // Link to the parent (if any). scope = scope->GetEnclosingScope(); if (scope) { - scopeInfo->SetParentScopeInfo(ScopeInfo::SaveScopeInfo(scope, scriptContext)); + scopeInfo->SetParentScopeInfo(ScopeInfo::SaveScopeInfo(byteCodeGenerator, scope, scriptContext)); } return scopeInfo; @@ -167,7 +167,7 @@ namespace Js currentScope = currentScope->GetEnclosingScope(); } - ScopeInfo * scopeInfo = ScopeInfo::SaveScopeInfo(currentScope, byteCodeGenerator->GetScriptContext()); + ScopeInfo * scopeInfo = ScopeInfo::SaveScopeInfo(byteCodeGenerator, currentScope, byteCodeGenerator->GetScriptContext()); if (scopeInfo != nullptr) { funcInfo->byteCodeFunction->SetScopeInfo(scopeInfo); diff --git a/lib/Runtime/ByteCode/ScopeInfo.h b/lib/Runtime/ByteCode/ScopeInfo.h index 9540cc8b098..a6e471fbd62 100644 --- a/lib/Runtime/ByteCode/ScopeInfo.h +++ b/lib/Runtime/ByteCode/ScopeInfo.h @@ -15,6 +15,7 @@ namespace Js { struct MapSymbolData { + ByteCodeGenerator *byteCodeGenerator; FuncInfo* func; int nonScopeSymbolCount; }; @@ -181,8 +182,8 @@ namespace Js { void SaveSymbolInfo(Symbol* sym, MapSymbolData* mapSymbolData); - static ScopeInfo* SaveScopeInfo(Scope * scope, ScriptContext * scriptContext); - static ScopeInfo* SaveOneScopeInfo(Scope * scope, ScriptContext * scriptContext); + static ScopeInfo* SaveScopeInfo(ByteCodeGenerator * byteCodeGenerator, Scope * scope, ScriptContext * scriptContext); + static ScopeInfo* SaveOneScopeInfo(ByteCodeGenerator * byteCodeGenerator, Scope * scope, ScriptContext * scriptContext); public: FunctionInfo * GetFunctionInfo() const diff --git a/lib/Runtime/ByteCode/Symbol.cpp b/lib/Runtime/ByteCode/Symbol.cpp index 8f83191df67..8e61ba01a72 100644 --- a/lib/Runtime/ByteCode/Symbol.cpp +++ b/lib/Runtime/ByteCode/Symbol.cpp @@ -64,18 +64,18 @@ void Symbol::SaveToPropIdArray(Symbol *sym, Js::PropertyIdArray *propIds, ByteCo } } -bool Symbol::NeedsSlotAlloc(FuncInfo *funcInfo) +bool Symbol::NeedsSlotAlloc(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo) { - return IsInSlot(funcInfo, true); + return IsInSlot(byteCodeGenerator, funcInfo, true); } -bool Symbol::IsInSlot(FuncInfo *funcInfo, bool ensureSlotAlloc) +bool Symbol::IsInSlot(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool ensureSlotAlloc) { if (this->GetIsGlobal() || this->GetIsModuleExportStorage()) { return false; } - if (funcInfo->GetHasHeapArguments() && this->GetIsFormal() && ByteCodeGenerator::NeedScopeObjectForArguments(funcInfo, funcInfo->root)) + if (funcInfo->GetHasHeapArguments() && this->GetIsFormal() && byteCodeGenerator->NeedScopeObjectForArguments(funcInfo, funcInfo->root)) { return true; } @@ -100,9 +100,9 @@ bool Symbol::GetIsCommittedToSlot() const return isCommittedToSlot || this->scope->GetFunc()->GetCallsEval() || this->scope->GetFunc()->GetChildCallsEval(); } -Js::PropertyId Symbol::EnsureScopeSlot(FuncInfo *funcInfo) +Js::PropertyId Symbol::EnsureScopeSlot(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo) { - if (this->NeedsSlotAlloc(funcInfo) && this->scopeSlot == Js::Constants::NoProperty) + if (this->NeedsSlotAlloc(byteCodeGenerator, funcInfo) && this->scopeSlot == Js::Constants::NoProperty) { this->scopeSlot = this->scope->AddScopeSlot(); } diff --git a/lib/Runtime/ByteCode/Symbol.h b/lib/Runtime/ByteCode/Symbol.h index bf02a23b377..d41a8657626 100644 --- a/lib/Runtime/ByteCode/Symbol.h +++ b/lib/Runtime/ByteCode/Symbol.h @@ -480,9 +480,9 @@ class Symbol return this->name; } - Js::PropertyId EnsureScopeSlot(FuncInfo *funcInfo); - bool IsInSlot(FuncInfo *funcInfo, bool ensureSlotAlloc = false); - bool NeedsSlotAlloc(FuncInfo *funcInfo); + Js::PropertyId EnsureScopeSlot(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo); + bool IsInSlot(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool ensureSlotAlloc = false); + bool NeedsSlotAlloc(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo); static void SaveToPropIdArray(Symbol *sym, Js::PropertyIdArray *propIds, ByteCodeGenerator *byteCodeGenerator, Js::PropertyId *pFirstSlot = nullptr); diff --git a/lib/Runtime/Language/AsmJsModule.cpp b/lib/Runtime/Language/AsmJsModule.cpp index 9bf1bcbf6cd..2aba048e6b3 100644 --- a/lib/Runtime/Language/AsmJsModule.cpp +++ b/lib/Runtime/Language/AsmJsModule.cpp @@ -124,7 +124,7 @@ namespace Js if (funcInfo->byteCodeFunction->GetIsNamedFunctionExpression()) { Assert(GetModuleFunctionNode()->sxFnc.pnodeName); - if (GetModuleFunctionNode()->sxFnc.pnodeName->sxVar.sym->IsInSlot(funcInfo)) + if (GetModuleFunctionNode()->sxFnc.pnodeName->sxVar.sym->IsInSlot(GetByteCodeGenerator(), funcInfo)) { ParseNodePtr nameNode = GetModuleFunctionNode()->sxFnc.pnodeName; GetByteCodeGenerator()->AssignPropertyId(nameNode->name()); diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 9300699cc54..49955a39eb8 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -1296,10 +1296,20 @@ namespace Js // In the debug mode zero out the local slot, so this could prevent locals being uninitialized in the case of setNextStatement. memset(newInstance->m_localSlots, 0, sizeof(Js::Var) * localCount); } - // Zero out only the return slot. This is not a user local, so the byte code will not initialize - // it to "undefined". And it's not an expression temp, so, for instance, a jitted loop body may expect - // it to be valid on entry to the loop, where "valid" means either a var or null. - newInstance->SetNonVarReg(0, NULL); + else + { + Js::RegSlot varCount = function->GetFunctionBody()->GetVarCount(); + if (varCount) + { + // Zero out the non-constant var slots. + Js::RegSlot constantCount = function->GetFunctionBody()->GetConstantCount(); + memset(newInstance->m_localSlots + constantCount, 0, varCount * sizeof(Js::Var)); + } + // Zero out the return slot. This is not a user local, so the byte code will not initialize + // it to "undefined". And it's not an expression temp, so, for instance, a jitted loop body may expect + // it to be valid on entry to the loop, where "valid" means either a var or null. + newInstance->SetNonVarReg(0, NULL); + } #endif // Wasm doesn't use const table if (!executeFunction->IsWasmFunction()) From 91dc6f8599979f2beba46e329be70f0788fa0889 Mon Sep 17 00:00:00 2001 From: Matthew McGovern Date: Wed, 4 Apr 2018 15:37:37 -0700 Subject: [PATCH 6/8] [CVE-2018-0990] Edge - UAF leads to potential RCE in latest version - Qihoo 360 --- lib/Runtime/Types/DictionaryTypeHandler.cpp | 55 +++++++++++++-------- lib/Runtime/Types/DictionaryTypeHandler.h | 2 +- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/lib/Runtime/Types/DictionaryTypeHandler.cpp b/lib/Runtime/Types/DictionaryTypeHandler.cpp index 7dd7a1432a6..cd3641b73ba 100644 --- a/lib/Runtime/Types/DictionaryTypeHandler.cpp +++ b/lib/Runtime/Types/DictionaryTypeHandler.cpp @@ -716,9 +716,14 @@ namespace Js template template - void DictionaryTypeHandlerBase::SetPropertyWithDescriptor(DynamicObject* instance, PropertyId propertyId, DictionaryPropertyDescriptor * descriptor, + void DictionaryTypeHandlerBase::SetPropertyWithDescriptor(DynamicObject* instance, + PropertyRecord const* propertyRecord, + DictionaryPropertyDescriptor ** pdescriptor, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) { + Assert(pdescriptor && *pdescriptor); + DictionaryPropertyDescriptor * descriptor = *pdescriptor; + PropertyId propertyId = propertyRecord->GetPropertyId(); Assert(instance); Assert((descriptor->Attributes & PropertyDeleted) == 0 || (allowLetConstGlobal && descriptor->IsShadowed)); @@ -784,14 +789,23 @@ namespace Js // Wait for the setter to return before setting up the inline cache info, as the setter may change // the attributes - T dataSlot = descriptor->template GetDataPropertyIndex(); - if (dataSlot != NoSlots) + + if (propertyMap->TryGetReference(propertyRecord, pdescriptor)) { - SetPropertyValueInfo(info, instance, dataSlot, descriptor->Attributes); + descriptor = *pdescriptor; + T dataSlot = descriptor->template GetDataPropertyIndex(); + if (dataSlot != NoSlots) + { + SetPropertyValueInfo(info, instance, dataSlot, descriptor->Attributes); + } + else if (descriptor->GetSetterPropertyIndex() != NoSlots) + { + SetPropertyValueInfo(info, instance, descriptor->GetSetterPropertyIndex(), descriptor->Attributes, InlineCacheSetterFlag); + } } - else if (descriptor->GetSetterPropertyIndex() != NoSlots) + else { - SetPropertyValueInfo(info, instance, descriptor->GetSetterPropertyIndex(), descriptor->Attributes, InlineCacheSetterFlag); + *pdescriptor = nullptr; } } SetPropertyUpdateSideEffect(instance, propertyId, value, SideEffects_Any); @@ -854,7 +868,7 @@ namespace Js { descriptor->ConvertToData(); } - SetPropertyWithDescriptor(instance, propertyId, descriptor, value, flags, info); + SetPropertyWithDescriptor(instance, propertyRecord, &descriptor, value, flags, info); return true; } @@ -1895,25 +1909,26 @@ namespace Js if (attributes & PropertyLetConstGlobal) { - SetPropertyWithDescriptor(instance, propertyId, descriptor, value, flags, info); + SetPropertyWithDescriptor(instance, propertyRecord, &descriptor, value, flags, info); } else { - SetPropertyWithDescriptor(instance, propertyId, descriptor, value, flags, info); + SetPropertyWithDescriptor(instance, propertyRecord, &descriptor, value, flags, info); } - - if (descriptor->Attributes & PropertyEnumerable) + if (descriptor != nullptr) //descriptor can dissappear, so this reference may not exist. { - instance->SetHasNoEnumerableProperties(false); - } - - if (!(descriptor->Attributes & PropertyWritable)) - { - this->ClearHasOnlyWritableDataProperties(); - if(GetFlags() & IsPrototypeFlag) + if (descriptor->Attributes & PropertyEnumerable) { - scriptContext->InvalidateStoreFieldCaches(propertyId); - instance->GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties(); + instance->SetHasNoEnumerableProperties(false); + } + if (!(descriptor->Attributes & PropertyWritable)) + { + this->ClearHasOnlyWritableDataProperties(); + if (GetFlags() & IsPrototypeFlag) + { + scriptContext->InvalidateStoreFieldCaches(propertyId); + instance->GetLibrary()->NoPrototypeChainsAreEnsuredToHaveOnlyWritableDataProperties(); + } } } diff --git a/lib/Runtime/Types/DictionaryTypeHandler.h b/lib/Runtime/Types/DictionaryTypeHandler.h index 86a40e8c9cb..813be74d3ea 100644 --- a/lib/Runtime/Types/DictionaryTypeHandler.h +++ b/lib/Runtime/Types/DictionaryTypeHandler.h @@ -247,7 +247,7 @@ namespace Js template inline DescriptorFlags GetSetterFromDescriptor(DynamicObject* instance, DictionaryPropertyDescriptor * descriptor, Var* setterValue, PropertyValueInfo* info); template - inline void SetPropertyWithDescriptor(DynamicObject* instance, PropertyId propertyId, DictionaryPropertyDescriptor * descriptor, + inline void SetPropertyWithDescriptor(DynamicObject* instance, PropertyRecord const* propertyRecord, DictionaryPropertyDescriptor ** pdescriptor, Var value, PropertyOperationFlags flags, PropertyValueInfo* info); protected: From 0578ca55215d2eda74280e17c4b9bcc3c38dfd6a Mon Sep 17 00:00:00 2001 From: Akrosh Gandhi Date: Thu, 5 Apr 2018 10:16:48 -0700 Subject: [PATCH 7/8] [CVE-2018-0994] Edge - Submit a use after free bug to Edge - 360Vulcan --- lib/Runtime/Library/JavascriptArray.cpp | 9 +++++++++ lib/Runtime/Library/JavascriptArray.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index e03734a72c2..c520f0996e4 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -2090,6 +2090,13 @@ namespace Js // Code below has potential to throw due to OOM or SO. Just FailFast on those cases AutoDisableInterrupt failFastError(scriptContext->GetThreadContext()); +#if defined(TARGET_32) + if (fArray->head && (fArray->head->size >= SparseArraySegmentBase::INLINE_CHUNK_SIZE / shrinkFactor)) + { + CopyHeadIfInlinedHeadSegment(fArray, recycler); + } +#endif + for (seg = fArray->head; seg; seg = nextSeg) { nextSeg = seg->next; @@ -5320,6 +5327,8 @@ namespace Js AnalysisAssert(array->head); SparseArraySegment* newHeadSeg = array->ReallocNonLeafSegment((SparseArraySegment*)PointerValue(array->head), array->head->next); array->head = newHeadSeg; + array->InvalidateLastUsedSegment(); + array->ClearSegmentMap(); } } diff --git a/lib/Runtime/Library/JavascriptArray.h b/lib/Runtime/Library/JavascriptArray.h index f1c7c041a70..a399d7d1ef5 100644 --- a/lib/Runtime/Library/JavascriptArray.h +++ b/lib/Runtime/Library/JavascriptArray.h @@ -549,6 +549,8 @@ namespace Js void SetHeadAndLastUsedSegment(SparseArraySegmentBase * segment); void SetLastUsedSegment(SparseArraySegmentBase * segment); bool HasSegmentMap() const; + template + static void CopyHeadIfInlinedHeadSegment(JavascriptArray *array, Recycler *recycler); private: void SetSegmentMap(SegmentBTreeRoot * segmentMap); @@ -584,8 +586,6 @@ namespace Js virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext); - template - static void CopyHeadIfInlinedHeadSegment(JavascriptArray *array, Recycler *recycler); template static void ReallocateNonLeafLastSegmentIfLeaf(JavascriptArray * arr, Recycler * recycler); From e8a1e295fc8336d09fb2b7a8686b8b9c930caa62 Mon Sep 17 00:00:00 2001 From: Rajat Dua Date: Tue, 10 Apr 2018 11:03:30 -0700 Subject: [PATCH 8/8] Updating ChakraCore version --- Build/NuGet/.pack-version | 2 +- lib/Common/ChakraCoreVersion.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build/NuGet/.pack-version b/Build/NuGet/.pack-version index 53adb84c822..a7ee35a3ea7 100644 --- a/Build/NuGet/.pack-version +++ b/Build/NuGet/.pack-version @@ -1 +1 @@ -1.8.2 +1.8.3 diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index cca2d70bda3..43adb163722 100644 --- a/lib/Common/ChakraCoreVersion.h +++ b/lib/Common/ChakraCoreVersion.h @@ -17,7 +17,7 @@ // ChakraCore version number definitions (used in ChakraCore binary metadata) #define CHAKRA_CORE_MAJOR_VERSION 1 #define CHAKRA_CORE_MINOR_VERSION 8 -#define CHAKRA_CORE_PATCH_VERSION 2 +#define CHAKRA_CORE_PATCH_VERSION 3 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // -------------