From 40e45fc38189cc021267c65d42ca2fb5f899f9de Mon Sep 17 00:00:00 2001 From: "Thomas Moore (CHAKRA)" Date: Wed, 13 Dec 2017 11:59:08 -0800 Subject: [PATCH] [CVE-2018-0776] JIT: stack-to-heap copy bug - Google, Inc. This change fixes a type-confusion bug that can occur with Native arrays allocated on the stack. Once JIT'd code expects a Native array to be used on the stack, the POC converts it to a Var array. This is combined with current behavior of the Arguments property, which moves the array from the stack to the heap. The result of these two assumptions is natively setting a Float value where a Var value is expected, letting any arbitrary floating-point number be written to memory and subsequently accessed as a Var. This fix forces a deep copy of Arrays that are returned via Arguments. This ensures that the new object created points to its own buffers. This also indicates a divergence with the original object and the one created by Arguments; however, there is currently no standard to define this behavior. --- lib/Backend/BailOut.cpp | 8 +-- lib/Backend/InlineeFrameInfo.cpp | 17 +++-- lib/Backend/InlineeFrameInfo.h | 6 +- lib/Runtime/Debug/DiagObjectModel.h | 2 +- lib/Runtime/Debug/DiagStackFrame.cpp | 2 +- lib/Runtime/Language/JavascriptOperators.cpp | 8 +-- lib/Runtime/Language/JavascriptOperators.h | 2 +- .../Language/JavascriptStackWalker.cpp | 32 ++++++--- lib/Runtime/Language/JavascriptStackWalker.h | 7 +- lib/Runtime/Library/JavascriptArray.cpp | 71 +++++++++++++------ lib/Runtime/Library/JavascriptArray.h | 16 ++--- lib/Runtime/Library/JavascriptFunction.cpp | 1 + 12 files changed, 112 insertions(+), 60 deletions(-) diff --git a/lib/Backend/BailOut.cpp b/lib/Backend/BailOut.cpp index c026951859d..a784a37008a 100644 --- a/lib/Backend/BailOut.cpp +++ b/lib/Backend/BailOut.cpp @@ -1006,7 +1006,7 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack if (boxStackInstance) { Js::Var oldValue = value; - value = Js::JavascriptOperators::BoxStackInstance(oldValue, scriptContext, /* allowStackFunction */ true); + value = Js::JavascriptOperators::BoxStackInstance(oldValue, scriptContext, /* allowStackFunction */ true, /* deepCopy */ false); if (oldValue != value) { @@ -1275,7 +1275,7 @@ BailOutRecord::BailOutInlinedHelper(Js::JavascriptCallStackLayout * layout, Bail if (inlineeFrameRecord) { InlinedFrameLayout* outerMostFrame = (InlinedFrameLayout *)(((uint8 *)Js::JavascriptCallStackLayout::ToFramePointer(layout)) - entryPointInfo->frameHeight); - inlineeFrameRecord->RestoreFrames(functionBody, outerMostFrame, layout); + inlineeFrameRecord->RestoreFrames(functionBody, outerMostFrame, layout, false /* deepCopy */); } } @@ -1480,7 +1480,7 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF { const Js::Var arg = args.Values[i]; BAILOUT_VERBOSE_TRACE(executeFunction, bailOutKind, _u("BailOut: Argument #%3u: value: 0x%p"), i, arg); - const Js::Var boxedArg = Js::JavascriptOperators::BoxStackInstance(arg, functionScriptContext, true); + const Js::Var boxedArg = Js::JavascriptOperators::BoxStackInstance(arg, functionScriptContext, /* allowStackFunction */ true, /* deepCopy */ false); if(boxedArg != arg) { args.Values[i] = boxedArg; @@ -1775,7 +1775,7 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF aReturn = Js::JavascriptFunction::FinishConstructor(aReturn, args.Values[0], function); Js::Var oldValue = aReturn; - aReturn = Js::JavascriptOperators::BoxStackInstance(oldValue, functionScriptContext, /* allowStackFunction */ true); + aReturn = Js::JavascriptOperators::BoxStackInstance(oldValue, functionScriptContext, /* allowStackFunction */ true, /* deepCopy */ false); #if ENABLE_DEBUG_CONFIG_OPTIONS if (oldValue != aReturn) { diff --git a/lib/Backend/InlineeFrameInfo.cpp b/lib/Backend/InlineeFrameInfo.cpp index 97aa7bd41b6..b5e2d69c517 100644 --- a/lib/Backend/InlineeFrameInfo.cpp +++ b/lib/Backend/InlineeFrameInfo.cpp @@ -199,13 +199,14 @@ void InlineeFrameRecord::Finalize(Func* inlinee, uint32 currentOffset) Assert(this->inlineDepth != 0); } -void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout) const +void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout, bool deepCopy) const { Assert(this->inlineDepth != 0); Assert(inlineeStartOffset != 0); BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore function object: ")); - Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody); + // No deepCopy needed for just the function + Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody, /*deepCopy*/ false); Assert(Js::ScriptFunction::Is(varFunction)); Js::ScriptFunction* function = Js::ScriptFunction::FromVar(varFunction); @@ -219,7 +220,9 @@ void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLay bool isInt32 = losslessInt32Args.Test(i) != 0; BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore argument %d: "), i); - Js::Var var = this->Restore(this->argOffsets[i], isFloat64, isInt32, layout, functionBody); + // Forward deepCopy flag for the arguments in case their data must be guaranteed + // to have its own lifetime + Js::Var var = this->Restore(this->argOffsets[i], isFloat64, isInt32, layout, functionBody, deepCopy); #if DBG if (!Js::TaggedNumber::Is(var)) { @@ -233,7 +236,7 @@ void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLay BAILOUT_FLUSH(functionBody); } -void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack) +void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack, bool deepCopy) { InlineeFrameRecord* innerMostRecord = this; class AutoReverse @@ -271,7 +274,7 @@ void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFr while (currentRecord) { - currentRecord->Restore(functionBody, currentFrame, callstack); + currentRecord->Restore(functionBody, currentFrame, callstack, deepCopy); currentRecord = currentRecord->parent; currentFrame = currentFrame->Next(); } @@ -280,7 +283,7 @@ void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFr currentFrame->callInfo.Count = 0; } -Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody) const +Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool deepCopy) const { Js::Var value; bool boxStackInstance = true; @@ -322,7 +325,7 @@ Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js if (boxStackInstance) { Js::Var oldValue = value; - value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true); + value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true, deepCopy); #if ENABLE_DEBUG_CONFIG_OPTIONS if (oldValue != value) diff --git a/lib/Backend/InlineeFrameInfo.h b/lib/Backend/InlineeFrameInfo.h index 830551abf11..a5d4b10ac5a 100644 --- a/lib/Backend/InlineeFrameInfo.h +++ b/lib/Backend/InlineeFrameInfo.h @@ -108,7 +108,7 @@ struct InlineeFrameRecord } void PopulateParent(Func* func); - void RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostInlinee, Js::JavascriptCallStackLayout* callstack); + void RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostInlinee, Js::JavascriptCallStackLayout* callstack, bool deepCopy); void Finalize(Func* inlinee, uint currentOffset); #if DBG_DUMP void Dump() const; @@ -123,8 +123,8 @@ struct InlineeFrameRecord } private: - void Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *outerMostFrame, Js::JavascriptCallStackLayout * layout) const; - Js::Var Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody) const; + void Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *outerMostFrame, Js::JavascriptCallStackLayout * layout, bool deepCopy) const; + Js::Var Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool deepCopy) const; InlineeFrameRecord* Reverse(); }; diff --git a/lib/Runtime/Debug/DiagObjectModel.h b/lib/Runtime/Debug/DiagObjectModel.h index 12f44069f2b..ee3b86dc061 100644 --- a/lib/Runtime/Debug/DiagObjectModel.h +++ b/lib/Runtime/Debug/DiagObjectModel.h @@ -370,7 +370,7 @@ namespace Js { activeScopeObject->SetPropertyWithAttributes( resolveObject.propId, - JavascriptOperators::BoxStackInstance(resolveObject.obj, scriptContext), //The value escapes, box if necessary. + JavascriptOperators::BoxStackInstance(resolveObject.obj, scriptContext, /* allowStackFunction */ false, /* deepCopy */ false), //The value escapes, box if necessary. resolveObject.isConst ? PropertyConstDefaults : PropertyDynamicTypeDefaults, nullptr); } diff --git a/lib/Runtime/Debug/DiagStackFrame.cpp b/lib/Runtime/Debug/DiagStackFrame.cpp index 14611247fb8..0348c3a5c1b 100644 --- a/lib/Runtime/Debug/DiagStackFrame.cpp +++ b/lib/Runtime/Debug/DiagStackFrame.cpp @@ -348,7 +348,7 @@ namespace Js { activeScopeObject->SetPropertyWithAttributes( Js::PropertyIds::_this, - JavascriptOperators::BoxStackInstance(varThis, scriptContext), //The value escapes, box if necessary. + JavascriptOperators::BoxStackInstance(varThis, scriptContext, /* allowStackFunction */ false, /* deepCopy */ false), //The value escapes, box if necessary. PropertyConstDefaults, nullptr); #if DBG diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 4ed5cecaefa..23a3b6a32d0 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -9790,7 +9790,7 @@ namespace Js } Js::Var - JavascriptOperators::BoxStackInstance(Js::Var instance, ScriptContext * scriptContext, bool allowStackFunction) + JavascriptOperators::BoxStackInstance(Js::Var instance, ScriptContext * scriptContext, bool allowStackFunction, bool deepCopy) { if (!ThreadContext::IsOnStack(instance) || (allowStackFunction && !TaggedNumber::Is(instance) && (*(int*)instance & 1))) { @@ -9812,11 +9812,11 @@ namespace Js case Js::TypeIds_Object: return DynamicObject::BoxStackInstance(DynamicObject::FromVar(instance)); case Js::TypeIds_Array: - return JavascriptArray::BoxStackInstance(JavascriptArray::FromVar(instance)); + return JavascriptArray::BoxStackInstance(JavascriptArray::FromVar(instance), deepCopy); case Js::TypeIds_NativeIntArray: - return JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray::FromVar(instance)); + return JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray::FromVar(instance), deepCopy); case Js::TypeIds_NativeFloatArray: - return JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray::FromVar(instance)); + return JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray::FromVar(instance), deepCopy); case Js::TypeIds_Function: Assert(allowStackFunction); // Stack functions are deal with not mar mark them, but by nested function escape analysis diff --git a/lib/Runtime/Language/JavascriptOperators.h b/lib/Runtime/Language/JavascriptOperators.h index fd9e912d4ef..e229173b2e9 100644 --- a/lib/Runtime/Language/JavascriptOperators.h +++ b/lib/Runtime/Language/JavascriptOperators.h @@ -611,7 +611,7 @@ namespace Js #endif static RecyclableObject *GetCallableObjectOrThrow(const Var callee, ScriptContext *const scriptContext); - static Js::Var BoxStackInstance(Js::Var value, ScriptContext * scriptContext, bool allowStackFunction = false); + static Js::Var BoxStackInstance(Js::Var value, ScriptContext * scriptContext, bool allowStackFunction, bool deepCopy); static BOOL PropertyReferenceWalkUnscopable(Var instance, RecyclableObject** propertyObject, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext); static BOOL PropertyReferenceWalk(Var instance, RecyclableObject** propertyObject, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext); diff --git a/lib/Runtime/Language/JavascriptStackWalker.cpp b/lib/Runtime/Language/JavascriptStackWalker.cpp index 99c768e4b55..1a6eb32f616 100644 --- a/lib/Runtime/Language/JavascriptStackWalker.cpp +++ b/lib/Runtime/Language/JavascriptStackWalker.cpp @@ -450,7 +450,7 @@ namespace Js // are inlined frames on the stack the InlineeCallInfo of the first inlined frame // has the native offset of the current physical frame. Assert(!*inlinee); - InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(parentFunction), PreviousInterpreterFrameIsFromBailout(), loopNum, this, useInternalFrameInfo); + InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(parentFunction), PreviousInterpreterFrameIsFromBailout(), loopNum, this, useInternalFrameInfo, false /*noAlloc*/, false /*deepCopy*/); inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset(); tmpFrameWalker.Close(); } @@ -555,7 +555,8 @@ namespace Js } bool hasInlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, - ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/); + ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/, false /*noAlloc*/, this->deepCopyForArgs); + if (hasInlinedFramesOnStack) { // We're now back in the state where currentFrame == physical frame of the inliner, but @@ -602,7 +603,18 @@ namespace Js // Check whether there are inlined frames nested in this native frame. The corresponding check for // a jitted loop body frame should have been done in CheckJavascriptFrame Assert(lastInternalFrameInfo.codeAddress == nullptr); - if (InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, ScriptFunction::FromVar(function))) + bool inlinedFramesFound = InlinedFrameWalker::FromPhysicalFrame( + inlinedFrameWalker, + currentFrame, + ScriptFunction::FromVar(function), + false, // fromBailout + -1, // loopNum + nullptr,// walker + false, // useInternalFrameInfo + false, // noAlloc + this->deepCopyForArgs + ); + if (inlinedFramesFound) { this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo); this->hasInlinedFramesOnStack = true; @@ -621,7 +633,8 @@ namespace Js _NOINLINE JavascriptStackWalker::JavascriptStackWalker(ScriptContext * scriptContext, bool useEERContext, PVOID returnAddress, bool _forceFullWalk /*=false*/) : inlinedFrameCallInfo(CallFlags_None, 0), shouldDetectPartiallyInitializedInterpreterFrame(true), forceFullWalk(_forceFullWalk), - previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false) + previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false), + deepCopyForArgs(false) { if (scriptContext == NULL) { @@ -917,7 +930,7 @@ namespace Js Assert((this->interpreterFrame->GetFlags() & Js::InterpreterStackFrameFlags_FromBailOut) != 0); InlinedFrameWalker tmpFrameWalker; Assert(InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]), - true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/)); + true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/, false /*deepCopy*/)); tmpFrameWalker.Close(); } #endif //DBG @@ -964,9 +977,10 @@ namespace Js { if (includeInlineFrames && InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]), - false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/)) + false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, false /*noAlloc*/, this->deepCopyForArgs)) { // Found inlined frames in a jitted loop body. We dont want to skip the inlined frames; walk all of them before setting codeAddress on lastInternalFrameInfo. + // DeepCopy here because, if there is an inlinee in a loop body, FromPhysicalFrame won't be called from UpdateFrame this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo); this->hasInlinedFramesOnStack = true; Assert(inlinedFramesBeingWalked); @@ -1208,7 +1222,7 @@ namespace Js #if ENABLE_NATIVE_CODEGEN bool InlinedFrameWalker::FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout, - int loopNum, const JavascriptStackWalker * const stackWalker, bool useInternalFrameInfo, bool noAlloc) + int loopNum, const JavascriptStackWalker * const stackWalker, bool useInternalFrameInfo, bool noAlloc, bool deepCopy) { bool inlinedFramesFound = false; FunctionBody* parentFunctionBody = parent->GetFunctionBody(); @@ -1261,7 +1275,7 @@ namespace Js if (record) { - record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer)); + record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer), deepCopy); } } @@ -1347,7 +1361,7 @@ namespace Js for (size_t i = 0; i < argCount; i++) { - args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext); + args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext, false /*allowStackFunction*/, false /*deepCopy*/); } } diff --git a/lib/Runtime/Language/JavascriptStackWalker.h b/lib/Runtime/Language/JavascriptStackWalker.h index de3a5ee088b..9dd22540f98 100644 --- a/lib/Runtime/Language/JavascriptStackWalker.h +++ b/lib/Runtime/Language/JavascriptStackWalker.h @@ -96,8 +96,8 @@ namespace Js Assert(currentIndex == -1); } - static bool FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout = false, - int loopNum = -1, const JavascriptStackWalker * const walker = nullptr, bool useInternalFrameInfo = false, bool noAlloc = false); + static bool FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout, + int loopNum, const JavascriptStackWalker * const walker, bool useInternalFrameInfo, bool noAlloc, bool deepCopy); void Close(); bool Next(CallInfo& callInfo); size_t GetArgc() const; @@ -304,6 +304,8 @@ namespace Js { return previousInterpreterFrameIsFromBailout; } + + void SetDeepCopyForArguments() { deepCopyForArgs = true; } #if DBG static bool ValidateTopJitFrame(Js::ScriptContext* scriptContext); #endif @@ -328,6 +330,7 @@ namespace Js bool previousInterpreterFrameIsFromBailout : 1; bool previousInterpreterFrameIsForLoopBody : 1; bool forceFullWalk : 1; // ignoring hasCaller + bool deepCopyForArgs : 1; // indicates when Var's data should be deep-copied when gathering Arguments for the frame Var GetThisFromFrame() const; // returns 'this' object from the physical frame Var GetCurrentArgumentsObject() const; // returns arguments object from the current frame, which may be virtual (belonging to an inlinee) diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index f922d696549..5480b5ebc62 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -11782,28 +11782,59 @@ namespace Js #endif template - void JavascriptArray::InitBoxedInlineHeadSegment(SparseArraySegment * dst, SparseArraySegment * src) + void JavascriptArray::InitBoxedInlineSegments(SparseArraySegment * dst, SparseArraySegment * src, bool deepCopy) { // Don't copy the segment map, we will build it again SetFlags(GetFlags() & ~DynamicObjectFlags::HasSegmentMap); SetHeadAndLastUsedSegment(dst); + // Copy head segment data dst->left = src->left; dst->length = src->length; dst->size = src->size; dst->CheckLengthvsSize(); - dst->next = src->next; - CopyArray(dst->elements, dst->size, src->elements, src->size); + + if (!deepCopy) + { + // Without a deep copy, point to the existing next segment + dst->next = src->next; + } + else + { + // When deepCopy is true, make a separate copy of each segment. While this involves data + // duplication, it allows the new object to have a separate lifetime without sharing data. + AutoDisableInterrupt failFastError(GetScriptContext()->GetThreadContext()); + do + { + if (src->next != nullptr) + { + // Allocate a new segment in the destination and copy from src + src = static_cast*>(src->next); + + dst->next = dst->AllocateSegment(GetRecycler(), src->left, src->length, src->size, src->next); + dst = static_cast*>(dst->next); + + CopyArray(dst->elements, dst->size, src->elements, src->size); + } + else + { + // Terminate the loop + dst->next = nullptr; + dst = nullptr; + } + } while (dst != nullptr); + failFastError.Completed(); + } } - JavascriptArray::JavascriptArray(JavascriptArray * instance, bool boxHead) + JavascriptArray::JavascriptArray(JavascriptArray * instance, bool boxHead, bool deepCopy) : ArrayObject(instance) { if (boxHead) { - InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head)); + InitBoxedInlineSegments(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head), false); } else { @@ -11815,7 +11846,7 @@ namespace Js } template - T * JavascriptArray::BoxStackInstance(T * instance) + T * JavascriptArray::BoxStackInstance(T * instance, bool deepCopy) { Assert(ThreadContext::IsOnStack(instance)); // On the stack, the we reserved a pointer before the object as to store the boxed value @@ -11831,15 +11862,15 @@ namespace Js { boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize + sizeof(Js::SparseArraySegmentBase) + instance->head->size * sizeof(typename T::TElement), - T, instance, true); + T, instance, true, deepCopy); } else if(inlineSlotsSize) { - boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false); + boxedInstance = RecyclerNewPlusZ(instance->GetRecycler(), inlineSlotsSize, T, instance, false, false); } else { - boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false); + boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false, false); } *boxedInstanceRef = boxedInstance; @@ -11847,9 +11878,9 @@ namespace Js } JavascriptArray * - JavascriptArray::BoxStackInstance(JavascriptArray * instance) + JavascriptArray::BoxStackInstance(JavascriptArray * instance, bool deepCopy) { - return BoxStackInstance(instance); + return BoxStackInstance(instance, deepCopy); } #if ENABLE_TTD @@ -11917,17 +11948,17 @@ namespace Js #endif JavascriptNativeArray::JavascriptNativeArray(JavascriptNativeArray * instance) : - JavascriptArray(instance, false), + JavascriptArray(instance, false, false), weakRefToFuncBody(instance->weakRefToFuncBody) { } - JavascriptNativeIntArray::JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead) : + JavascriptNativeIntArray::JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead, bool deepCopy) : JavascriptNativeArray(instance) { if (boxHead) { - InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head)); + InitBoxedInlineSegments(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head), deepCopy); } else { @@ -11938,9 +11969,9 @@ namespace Js } JavascriptNativeIntArray * - JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray * instance) + JavascriptNativeIntArray::BoxStackInstance(JavascriptNativeIntArray * instance, bool deepCopy) { - return JavascriptArray::BoxStackInstance(instance); + return JavascriptArray::BoxStackInstance(instance, deepCopy); } #if ENABLE_TTD @@ -11968,12 +11999,12 @@ namespace Js #endif #endif - JavascriptNativeFloatArray::JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead) : + JavascriptNativeFloatArray::JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead, bool deepCopy) : JavascriptNativeArray(instance) { if (boxHead) { - InitBoxedInlineHeadSegment(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head)); + InitBoxedInlineSegments(DetermineInlineHeadSegmentPointer(this), SparseArraySegment::From(instance->head), deepCopy); } else { @@ -11984,9 +12015,9 @@ namespace Js } JavascriptNativeFloatArray * - JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray * instance) + JavascriptNativeFloatArray::BoxStackInstance(JavascriptNativeFloatArray * instance, bool deepCopy) { - return JavascriptArray::BoxStackInstance(instance); + return JavascriptArray::BoxStackInstance(instance, deepCopy); } #if ENABLE_TTD diff --git a/lib/Runtime/Library/JavascriptArray.h b/lib/Runtime/Library/JavascriptArray.h index 57197de4fe2..8b0632ffa70 100644 --- a/lib/Runtime/Library/JavascriptArray.h +++ b/lib/Runtime/Library/JavascriptArray.h @@ -425,7 +425,7 @@ namespace Js JavascriptArray(uint32 length, DynamicType * type); // For BoxStackInstance - JavascriptArray(JavascriptArray * instance, bool boxHead); + JavascriptArray(JavascriptArray * instance, bool boxHead, bool deepCopy); template inline void LinkSegments(SparseArraySegment* prev, SparseArraySegment* current); template inline SparseArraySegment* ReallocNonLeafSegment(SparseArraySegment* seg, SparseArraySegmentBase* nextSeg, bool forceNonLeaf = false); @@ -897,11 +897,11 @@ namespace Js static Var SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray *spreadIndices, ScriptContext *scriptContext); static uint32 GetSpreadArgLen(Var spreadArg, ScriptContext *scriptContext); - static JavascriptArray * BoxStackInstance(JavascriptArray * instance); + static JavascriptArray * BoxStackInstance(JavascriptArray * instance, bool deepCopy); protected: - template void InitBoxedInlineHeadSegment(SparseArraySegment * dst, SparseArraySegment * src); + template void InitBoxedInlineSegments(SparseArraySegment * dst, SparseArraySegment * src, bool deepCopy); - template static T * BoxStackInstance(T * instance); + template static T * BoxStackInstance(T * instance, bool deepCopy); public: template static size_t DetermineAllocationSize(const uint inlineElementSlots, size_t *const allocationPlusSizeRef = nullptr, uint *const alignedInlineElementSlotsRef = nullptr); @@ -1019,7 +1019,7 @@ namespace Js JavascriptNativeArray(length, type) {} // For BoxStackInstance - JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead); + JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead, bool deepCopy); public: static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...); static Var NewInstance(RecyclableObject* function, Arguments args); @@ -1077,7 +1077,7 @@ namespace Js return LibraryValue::ValueNativeIntArrayType; } static DynamicType * GetInitialType(ScriptContext * scriptContext); - static JavascriptNativeIntArray * BoxStackInstance(JavascriptNativeIntArray * instance); + static JavascriptNativeIntArray * BoxStackInstance(JavascriptNativeIntArray * instance, bool deepCopy); private: virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext) override; @@ -1182,7 +1182,7 @@ namespace Js JavascriptNativeArray(length, type) {} // For BoxStackInstance - JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead); + JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead, bool deepCopy); public: static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...); @@ -1244,7 +1244,7 @@ namespace Js static DynamicType * GetInitialType(ScriptContext * scriptContext); static Var Push(ScriptContext * scriptContext, Var * nativeFloatArray, double value); - static JavascriptNativeFloatArray * BoxStackInstance(JavascriptNativeFloatArray * instance); + static JavascriptNativeFloatArray * BoxStackInstance(JavascriptNativeFloatArray * instance, bool deepCopy); static double Pop(ScriptContext * scriptContext, Var nativeFloatArray); private: virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext) override; diff --git a/lib/Runtime/Library/JavascriptFunction.cpp b/lib/Runtime/Library/JavascriptFunction.cpp index 23f883981f3..32b2b4038c2 100644 --- a/lib/Runtime/Library/JavascriptFunction.cpp +++ b/lib/Runtime/Library/JavascriptFunction.cpp @@ -2885,6 +2885,7 @@ void __cdecl _alloca_probe_16() // and foo.arguments[n] will be maintained after this object is returned. JavascriptStackWalker walker(scriptContext); + walker.SetDeepCopyForArguments(); if (walker.WalkToTarget(this)) {