CVE-2018-0934
Description
Chakra scripting engine memory corruption in Microsoft Edge and ChakraCore allows remote code execution via crafted web content.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Chakra scripting engine memory corruption in Microsoft Edge and ChakraCore allows remote code execution via crafted web content.
Vulnerability
A memory corruption vulnerability exists in the Chakra scripting engine, specifically in the JIT compiler's handling of stack-to-heap copies for JavaScript arrays. The flaw resides in JavascriptArray::BoxStackInstance and JavascriptStackWalker::GetJavascriptArgs functions, where an incomplete fix for a prior issue (MSRC-41913) left a code path that could be exploited. Affected versions include ChakraCore and Microsoft Edge on Windows 10 Gold, 1511, 1607, 1703, 1709, and Windows Server 2016 [1][2].
Exploitation
An attacker can host a malicious website and lure a victim to visit it using Microsoft Edge. The exploit leverages the Error constructor, which iterates over all functions and arguments in the call stack and invokes BoxStackInstance with deepCopy set to false. This causes a shallow copy of a stack-allocated array to be cached. On subsequent calls, the cached shallow copy is returned, leading to a use-after-free condition when the original stack object is freed [3]. No authentication or user interaction beyond browsing is required [4].
Impact
Successful exploitation allows remote code execution in the context of the current user. An attacker can gain the same user privileges, potentially leading to full system compromise if the user is an administrator [1][4].
Mitigation
Microsoft released security updates as part of the March 2018 Patch Tuesday (March 13, 2018). Users should apply the latest updates for affected Windows versions and ChakraCore. The fix is also available in the ChakraCore repository commit 6d0f5de [2]. No workarounds are available; updating is the only mitigation [1][4].
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
Microsoft.ChakraCoreNuGet | < 1.8.2 | 1.8.2 |
Affected products
2- Microsoft Corporation/ChakraCore, Microsoft Edgev5Range: ChakraCore, Microsoft Windows 10 Gold, 1511, 1607, 1703, 1709, and Windows Server 2016.
Patches
16d0f5de1e033[CVE-2018-0934] Chakra JIT - Incomplete fix for MSRC-41913 #2 - Google, Inc.
11 files changed · +90 −121
lib/Backend/BailOut.cpp+4 −1 modified@@ -1301,8 +1301,11 @@ BailOutRecord::BailOutInlinedHelper(Js::JavascriptCallStackLayout * layout, Bail InlineeFrameRecord* inlineeFrameRecord = entryPointInfo->FindInlineeFrame(returnAddress); if (inlineeFrameRecord) { + // While bailing out, RestoreFrames should box all Vars on the stack. If there are multiple Vars pointing to the same + // object, the cached version (that was previously boxed) will be reused to maintain pointer identity and correctness + // after the transition to the interpreter. InlinedFrameLayout* outerMostFrame = (InlinedFrameLayout *)(((uint8 *)Js::JavascriptCallStackLayout::ToFramePointer(layout)) - entryPointInfo->frameHeight); - inlineeFrameRecord->RestoreFrames(functionBody, outerMostFrame, layout, false /* deepCopy */); + inlineeFrameRecord->RestoreFrames(functionBody, outerMostFrame, layout, true /* boxArgs */); } }
lib/Backend/InlineeFrameInfo.cpp+15 −9 modified@@ -199,14 +199,14 @@ void InlineeFrameRecord::Finalize(Func* inlinee, uint32 currentOffset) Assert(this->inlineDepth != 0); } -void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout, bool deepCopy) const +void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *inlinedFrame, Js::JavascriptCallStackLayout * layout, bool boxValues) const { Assert(this->inlineDepth != 0); Assert(inlineeStartOffset != 0); BAILOUT_VERBOSE_TRACE(functionBody, _u("Restore function object: ")); // No deepCopy needed for just the function - Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody, /*deepCopy*/ false); + Js::Var varFunction = this->Restore(this->functionOffset, /*isFloat64*/ false, /*isInt32*/ false, layout, functionBody, boxValues); Assert(Js::ScriptFunction::Is(varFunction)); Js::ScriptFunction* function = Js::ScriptFunction::FromVar(varFunction); @@ -222,9 +222,9 @@ void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLay // 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); + Js::Var var = this->Restore(this->argOffsets[i], isFloat64, isInt32, layout, functionBody, boxValues); #if DBG - if (!Js::TaggedNumber::Is(var)) + if (boxValues && !Js::TaggedNumber::Is(var)) { Js::RecyclableObject *const recyclableObject = Js::RecyclableObject::FromVar(var); Assert(!ThreadContext::IsOnStack(recyclableObject)); @@ -236,7 +236,10 @@ void InlineeFrameRecord::Restore(Js::FunctionBody* functionBody, InlinedFrameLay BAILOUT_FLUSH(functionBody); } -void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack, bool deepCopy) +// Note: the boxValues parameter should be true when this is called from a Bailout codepath to ensure that multiple vars to +// the same object reuse the cached value during the transition to the interpreter. +// Otherwise, this parameter should be false as the values are not required to be moved to the heap to restore the frame. +void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostFrame, Js::JavascriptCallStackLayout* callstack, bool boxValues) { InlineeFrameRecord* innerMostRecord = this; class AutoReverse @@ -274,7 +277,7 @@ void InlineeFrameRecord::RestoreFrames(Js::FunctionBody* functionBody, InlinedFr while (currentRecord) { - currentRecord->Restore(functionBody, currentFrame, callstack, deepCopy); + currentRecord->Restore(functionBody, currentFrame, callstack, boxValues); currentRecord = currentRecord->parent; currentFrame = currentFrame->Next(); } @@ -283,10 +286,10 @@ 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, bool deepCopy) const +Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool boxValue) const { Js::Var value; - bool boxStackInstance = true; + bool boxStackInstance = boxValue; double dblValue; if (offset >= 0) { @@ -324,8 +327,11 @@ Js::Var InlineeFrameRecord::Restore(int offset, bool isFloat64, bool isInt32, Js BAILOUT_VERBOSE_TRACE(functionBody, _u(", value: 0x%p"), value); if (boxStackInstance) { + // Do not deepCopy in this call to BoxStackInstance because this should be used for + // bailing out, where a shallow copy that is cached is needed to ensure that multiple + // vars pointing to the same boxed object reuse the new boxed value. Js::Var oldValue = value; - value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true, deepCopy); + value = Js::JavascriptOperators::BoxStackInstance(oldValue, functionBody->GetScriptContext(), /* allowStackFunction */ true, false /* deepCopy */); #if ENABLE_DEBUG_CONFIG_OPTIONS if (oldValue != value)
lib/Backend/InlineeFrameInfo.h+3 −3 modified@@ -108,7 +108,7 @@ struct InlineeFrameRecord } void PopulateParent(Func* func); - void RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostInlinee, Js::JavascriptCallStackLayout* callstack, bool deepCopy); + void RestoreFrames(Js::FunctionBody* functionBody, InlinedFrameLayout* outerMostInlinee, Js::JavascriptCallStackLayout* callstack, bool boxValues); 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, bool deepCopy) const; - Js::Var Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool deepCopy) const; + void Restore(Js::FunctionBody* functionBody, InlinedFrameLayout *outerMostFrame, Js::JavascriptCallStackLayout * layout, bool boxValues) const; + Js::Var Restore(int offset, bool isFloat64, bool isInt32, Js::JavascriptCallStackLayout * layout, Js::FunctionBody* functionBody, bool boxValue) const; InlineeFrameRecord* Reverse(); };
lib/Runtime/Base/Debug.cpp+1 −1 modified@@ -43,7 +43,7 @@ WCHAR* DumpCallStackFull(uint frameCount, bool print) for (uint i = 0; i < callInfo.Count; i++) { - StringCchPrintf(buffer, _countof(buffer), _u(", 0x%p"), walker.GetJavascriptArgs()[i]); + StringCchPrintf(buffer, _countof(buffer), _u(", 0x%p"), walker.GetJavascriptArgs(false /*boxArgs*/)[i]); sb.AppendSz(buffer); } StringCchPrintf(buffer, _countof(buffer), _u(")[%s (%u, %d)]\n"), sourceFileName, line + 1, column + 1);
lib/Runtime/Language/JavascriptStackWalker.cpp+35 −17 modified@@ -263,14 +263,14 @@ namespace Js this->GetCurrentArgv()[JavascriptFunctionArgIndex_ArgumentsObject] = args; } - Js::Var * JavascriptStackWalker::GetJavascriptArgs() const + Js::Var * JavascriptStackWalker::GetJavascriptArgs(bool boxArgsAndDeepCopy) const { Assert(this->IsJavascriptFrame()); #if ENABLE_NATIVE_CODEGEN if (inlinedFramesBeingWalked) { - return inlinedFrameWalker.GetArgv(/* includeThis = */ false); + return inlinedFrameWalker.GetArgv(/* includeThis */ false, boxArgsAndDeepCopy); } else #endif @@ -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, false /*noAlloc*/, false /*deepCopy*/); + InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, ScriptFunction::FromVar(parentFunction), PreviousInterpreterFrameIsFromBailout(), loopNum, this, useInternalFrameInfo, false /*noAlloc*/); inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset(); tmpFrameWalker.Close(); } @@ -555,7 +555,7 @@ namespace Js } bool hasInlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, - ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/, false /*noAlloc*/, this->deepCopyForArgs); + ScriptFunction::FromVar(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/, false /*noAlloc*/); if (hasInlinedFramesOnStack) { @@ -611,8 +611,7 @@ namespace Js -1, // loopNum nullptr,// walker false, // useInternalFrameInfo - false, // noAlloc - this->deepCopyForArgs + false // noAlloc ); if (inlinedFramesFound) { @@ -658,8 +657,7 @@ 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), - deepCopyForArgs(false) + previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false) { if (scriptContext == NULL) { @@ -955,7 +953,7 @@ namespace Js Assert(this->interpreterFrame->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut)); InlinedFrameWalker tmpFrameWalker; Assert(InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]), - true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/, false /*deepCopy*/)); + true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/)); tmpFrameWalker.Close(); } #endif //DBG @@ -1002,7 +1000,7 @@ namespace Js { if (includeInlineFrames && InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, Js::ScriptFunction::FromVar(argv[JavascriptFunctionArgIndex_Function]), - false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, false /*noAlloc*/, this->deepCopyForArgs)) + false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, false /*noAlloc*/)) { // 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 @@ -1246,7 +1244,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, bool deepCopy) + int loopNum, const JavascriptStackWalker * const stackWalker, bool useInternalFrameInfo, bool noAlloc) { bool inlinedFramesFound = false; FunctionBody* parentFunctionBody = parent->GetFunctionBody(); @@ -1299,7 +1297,7 @@ namespace Js if (record) { - record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer), deepCopy); + record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer), false /* boxValues */); } } @@ -1366,26 +1364,46 @@ namespace Js return currentFrame->callInfo.Count; } - Js::Var *InlinedFrameWalker::GetArgv(bool includeThis /* = true */) const + // Note: the boxArgsAndDeepCopy parameter should be true when a copy of the JS args must be ensured to + // be on the heap. This results in a new array of Vars with deep copied boxed values (where + // appropriate). + // Otherwise, this parameter should be false. For instance, if the args will only be used + // internally to gather type info, the values are not boxed (so, some Vars may still be on + // the stack) and the array of the current frame is returned. + Js::Var *InlinedFrameWalker::GetArgv(bool includeThis, bool boxArgsAndDeepCopy) const { InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame(); Assert(currentFrame); uint firstArg = includeThis ? InlinedFrameArgIndex_This : InlinedFrameArgIndex_SecondScriptArg; - Js::Var *args = ¤tFrame->argv[firstArg]; + size_t argCount = this->GetArgc() - firstArg; + Js::Var *args; + if (!boxArgsAndDeepCopy) + { + args = ¤tFrame->argv[firstArg]; + + } + else + { + args = RecyclerNewArray(parentFunction->GetScriptContext()->GetRecycler(), Js::Var, argCount); + for (size_t i = 0; i < argCount; i++) + { + args[i] = currentFrame->argv[firstArg + i]; + } - this->FinalizeStackValues(args, this->GetArgc() - firstArg); + this->FinalizeStackValues(args, argCount, true /*deepCopy*/); + } return args; } - void InlinedFrameWalker::FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount) const + void InlinedFrameWalker::FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount, bool deepCopy) const { ScriptContext *scriptContext = this->GetFunctionObject()->GetScriptContext(); for (size_t i = 0; i < argCount; i++) { - args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext, false /*allowStackFunction*/, false /*deepCopy*/); + args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext, false /*allowStackFunction*/, deepCopy); } }
lib/Runtime/Language/JavascriptStackWalker.h+4 −6 modified@@ -97,11 +97,11 @@ namespace Js } static bool FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout, - int loopNum, const JavascriptStackWalker * const walker, bool useInternalFrameInfo, bool noAlloc, bool deepCopy); + int loopNum, const JavascriptStackWalker * const walker, bool useInternalFrameInfo, bool noAlloc); void Close(); bool Next(CallInfo& callInfo); size_t GetArgc() const; - Js::Var *GetArgv(bool includeThis = true) const; + Js::Var *GetArgv(bool includeThis, bool boxArgsAndDeepCopy) const; Js::JavascriptFunction *GetFunctionObject() const; void SetFunctionObject(Js::JavascriptFunction * function); Js::Var GetArgumentsObject() const; @@ -113,7 +113,7 @@ namespace Js uint32 GetCurrentInlineeOffset() const; uint32 GetBottomMostInlineeOffset() const; Js::JavascriptFunction *GetBottomMostFunctionObject() const; - void FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount) const; + void FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount, bool deepCopy) const; int32 GetFrameCount() { return frameCount; } private: @@ -218,7 +218,7 @@ namespace Js CallInfo GetCallInfo(bool includeInlinedFrames = true) const; CallInfo GetCallInfoFromPhysicalFrame() const; bool GetThis(Var *pThis, int moduleId) const; - Js::Var * GetJavascriptArgs() const; + Js::Var * GetJavascriptArgs(bool boxArgsAndDeepCopy) const; void **GetCurrentArgv() const; ScriptContext* GetCurrentScriptContext() const; @@ -310,7 +310,6 @@ namespace Js return previousInterpreterFrameIsFromBailout; } - void SetDeepCopyForArguments() { deepCopyForArgs = true; } #if DBG static bool ValidateTopJitFrame(Js::ScriptContext* scriptContext); #endif @@ -335,7 +334,6 @@ 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)
lib/Runtime/Language/StackTraceArguments.cpp+3 −1 modified@@ -73,7 +73,9 @@ namespace Js { if (numberOfArguments > 0) numberOfArguments --; // Don't consider 'this' for (int64 j = 0; j < numberOfArguments && j < MaxNumberOfDisplayedArgumentsInStack; j ++) { - types |= ObjectToTypeCode(walker.GetJavascriptArgs()[j]) << 3*j; // maximal code is 7, so we can use 3 bits to store it + // Since the Args are only used to get the type, no need to box the Vars to + // move them to the heap from the stack + types |= ObjectToTypeCode(walker.GetJavascriptArgs(false /*boxArgsAndDeepCopy*/)[j]) << 3 * j; // maximal code is 7, so we can use 3 bits to store it } if (numberOfArguments > MaxNumberOfDisplayedArgumentsInStack) {
lib/Runtime/Library/ArgumentsObject.cpp+1 −72 modified@@ -26,78 +26,7 @@ namespace Js stringBuilder->AppendCppLiteral(_u("Object, (Arguments)")); return TRUE; } - - Var ArgumentsObject::GetCaller(ScriptContext * scriptContext) - { - JavascriptStackWalker walker(scriptContext); - - if (!this->AdvanceWalkerToArgsFrame(&walker)) - { - return scriptContext->GetLibrary()->GetNull(); - } - - return ArgumentsObject::GetCaller(scriptContext, &walker, false); - } - - Var ArgumentsObject::GetCaller(ScriptContext * scriptContext, JavascriptStackWalker *walker, bool skipGlobal) - { - // The arguments.caller property is equivalent to callee.caller.arguments - that is, it's the - // caller's arguments object (if any). Just fetch the caller and compute its arguments. - JavascriptFunction* funcCaller = nullptr; - - while (walker->GetCaller(&funcCaller)) - { - if (walker->IsCallerGlobalFunction()) - { - // Caller is global/eval. If we're in IE9 mode, and the caller is eval, - // keep looking. Otherwise, caller is null. - if (skipGlobal || walker->IsEvalCaller()) - { - continue; - } - funcCaller = nullptr; - } - break; - } - - if (funcCaller == nullptr) - { - return scriptContext->GetLibrary()->GetNull(); - } - - AssertMsg(JavascriptOperators::GetTypeId(funcCaller) == TypeIds_Function, "non function caller"); - - const CallInfo callInfo = walker->GetCallInfo(); - uint32 paramCount = callInfo.Count; - CallFlags flags = callInfo.Flags; - - if (paramCount == 0 || (flags & CallFlags_Eval)) - { - // The caller is the "global function" or eval, so we return "null". - return scriptContext->GetLibrary()->GetNull(); - } - - if (!walker->GetCurrentFunction()->IsScriptFunction()) - { - // builtin function do not have an argument object - return null. - return scriptContext->GetLibrary()->GetNull(); - } - - // Create new arguments object, everytime this is requested for, with the actuals value. - Var args = nullptr; - - args = JavascriptOperators::LoadHeapArguments( - funcCaller, - paramCount - 1, - walker->GetJavascriptArgs(), - scriptContext->GetLibrary()->GetNull(), - scriptContext->GetLibrary()->GetNull(), - scriptContext, - /* formalsAreLetDecls */ false); - - return args; - } - + bool ArgumentsObject::Is(Var aValue) { return JavascriptOperators::GetTypeId(aValue) == TypeIds_Arguments;
lib/Runtime/Library/ArgumentsObject.h+0 −2 modified@@ -18,8 +18,6 @@ namespace Js { Assert(type->GetTypeId() == TypeIds_Arguments); } - Var GetCaller(ScriptContext * scriptContext); - static Var GetCaller(ScriptContext * scriptContext, JavascriptStackWalker *walker, bool skipGlobal); static bool Is(Var aValue);
lib/Runtime/Library/JavascriptArray.cpp+21 −6 modified@@ -11679,12 +11679,24 @@ namespace Js 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 - T ** boxedInstanceRef = ((T **)instance) - 1; - T * boxedInstance = *boxedInstanceRef; - if (boxedInstance) + T * boxedInstance; + T ** boxedInstanceRef; + if (!deepCopy) + { + // On the stack, the we reserved a pointer before the object as to store the boxed value + boxedInstanceRef = ((T **)instance) - 1; + boxedInstance = *boxedInstanceRef; + if (boxedInstance) + { + return boxedInstance; + } + } + else { - return boxedInstance; + // When doing a deep copy, do not cache the boxed value to ensure that only shallow copies + // are reused + boxedInstance = nullptr; + boxedInstanceRef = nullptr; } const size_t inlineSlotsSize = instance->GetTypeHandler()->GetInlineSlotsSize(); @@ -11703,7 +11715,10 @@ namespace Js boxedInstance = RecyclerNew(instance->GetRecycler(), T, instance, false, false); } - *boxedInstanceRef = boxedInstance; + if (boxedInstanceRef != nullptr) + { + *boxedInstanceRef = boxedInstance; + } return boxedInstance; }
lib/Runtime/Library/JavascriptFunction.cpp+3 −3 modified@@ -2871,7 +2871,6 @@ 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)) { @@ -2882,12 +2881,13 @@ void __cdecl _alloca_probe_16() else { Var args = nullptr; - //Create a copy of the arguments and return it. + // Since the arguments will be returned back to script, box the arguments to ensure a copy of + // them with their own lifetime (as well as move any from the stack to the heap). const CallInfo callInfo = walker.GetCallInfo(); args = JavascriptOperators::LoadHeapArguments( this, callInfo.Count - 1, - walker.GetJavascriptArgs(), + walker.GetJavascriptArgs(true /* boxArgsAndDeepCopy */), scriptContext->GetLibrary()->GetNull(), scriptContext->GetLibrary()->GetNull(), scriptContext,
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- www.exploit-db.com/exploits/44396/mitreexploitx_refsource_EXPLOIT-DB
- www.exploit-db.com/exploits/44397/mitreexploitx_refsource_EXPLOIT-DB
- github.com/advisories/GHSA-phcc-frh9-q545ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-0934ghsaADVISORY
- www.securityfocus.com/bid/103275mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1040507mitrevdb-entryx_refsource_SECTRACK
- github.com/chakra-core/ChakraCore/commit/6d0f5de1e0331fd6dd4f1ee0c0032430bcb131c3ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-0934ghsax_refsource_CONFIRMWEB
- web.archive.org/web/20210125205549/http://www.securityfocus.com/bid/103275ghsaWEB
- web.archive.org/web/20211026192005/http://www.securitytracker.com/id/1040507ghsaWEB
- www.exploit-db.com/exploits/44396ghsaWEB
- www.exploit-db.com/exploits/44397ghsaWEB
News mentions
0No linked articles in our index yet.