CVE-2018-8541
Description
Remote code execution in Chakra scripting engine via memory corruption when handling objects in Microsoft Edge and ChakraCore.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Remote code execution in Chakra scripting engine via memory corruption when handling objects in Microsoft Edge and ChakraCore.
Vulnerability
The Chakra scripting engine in Microsoft Edge and ChakraCore mishandles objects in memory, leading to memory corruption. This affects Microsoft Edge on Windows 10 versions and ChakraCore prior to the November 2018 security update [1][2].
Exploitation
An attacker can host a specially crafted website that triggers the vulnerability when visited using affected Microsoft Edge. No authentication is required; user interaction is needed to visit the malicious page [4].
Impact
Successful exploitation allows remote code execution in the context of the current user, potentially leading to full system compromise [1][2].
Mitigation
Microsoft released a security update in November 2018 as part of the monthly rollup for Windows 10. ChakraCore users should update to the patched version [4]. No workaround is available.
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.11.3 | 1.11.3 |
Affected products
3- Range: ChakraCore
Patches
12 files changed · +170 −138
lib/Runtime/Language/InterpreterStackFrame.cpp+4 −0 modified@@ -6727,6 +6727,10 @@ namespace Js // Finally exited with LeaveNull, We don't throw for early returns if (finallyEndOffset == 0 && exceptionObj) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + JavascriptExceptionOperators::WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(const_cast<Js::JavascriptExceptionObject *>(exceptionObj), scriptContext); } if (finallyEndOffset != 0)
lib/Runtime/Language/JavascriptExceptionOperators.cpp+166 −138 modified@@ -190,19 +190,23 @@ namespace Js { void *tryContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)frame + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize); - - try - { - tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); - } - catch (const Js::JavascriptException& err) { - exception = err.GetAndClear(); + void * addrOfReturnAddr = (void*)((char*)frame + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { + tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - if (exception) { // Clone static exception object early in case finally block overwrites it @@ -212,19 +216,9 @@ namespace Js if (exception) { #if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } - } - else + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, frame); - } + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); } #endif bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative @@ -251,21 +245,31 @@ namespace Js void *tryContinuation = nullptr; void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + spillSize + argsSize); - try - { - tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); - } - catch (const Js::JavascriptException& err) { - exception = err.GetAndClear(); + void * addrOfReturnAddr = (void*)((char*)frame + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { + tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize); + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - if (exception) { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } } finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize); @@ -276,6 +280,10 @@ namespace Js if (exception) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(exception, scriptContext); } @@ -365,44 +373,40 @@ namespace Js int hasBailedOutOffset, ScriptContext *scriptContext) { - void *tryContinuation = nullptr; - JavascriptExceptionObject *exception = nullptr; + void *tryContinuation = nullptr; + JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; + Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)localsPtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize); - try { + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try + { #if defined(_M_ARM) tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #elif defined(_M_ARM64) tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #endif - } - catch (const Js::JavascriptException& err) - { - exception = err.GetAndClear(); - } - - if (exception) - { -#if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } } - else + catch (const Js::JavascriptException& err) { - if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr); - } + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); } -#endif + } + if (exception) + { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } + bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative if (hasBailedOut) { @@ -437,26 +441,36 @@ namespace Js void *tryContinuation = nullptr; void *finallyContinuation = nullptr; JavascriptExceptionObject *exception = nullptr; + void *tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout + argsSize); - - try { + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + + try + { #if defined(_M_ARM) - tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); + tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #elif defined(_M_ARM64) - tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); + tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize); #endif + } + catch (const Js::JavascriptException& err) + { + exception = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch (const Js::JavascriptException& err) - { - exception = err.GetAndClear(); - } - if (exception) { // Clone static exception object early in case finally block overwrites it exception = exception->CloneIfStaticExceptionObject(scriptContext); + + if (exception->GetExceptionContext() && exception->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } } #if defined(_M_ARM) @@ -472,6 +486,10 @@ namespace Js if (exception) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(exception, scriptContext); } @@ -647,21 +665,24 @@ namespace Js { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; + void* tryHandlerAddrOfReturnAddr = nullptr; Js::JavascriptExceptionOperators::HasBailedOutPtrStack hasBailedOutPtrStack(scriptContext, (bool*)((char*)framePtr + hasBailedOutOffset)); PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout); - - try { - // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function - // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. - // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). - // In order to circumvent that we are throwing OutOfMemory. - if (!tryAddr) + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + try { - Assert(false); - ThrowOutOfMemory(scriptContext); - } + // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function + // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. + // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). + // In order to circumvent that we are throwing OutOfMemory. + if (!tryAddr) + { + Assert(false); + ThrowOutOfMemory(scriptContext); + } #ifdef _M_IX86 void *savedEsp; @@ -711,28 +732,19 @@ namespace Js #else AssertMsg(FALSE, "Unsupported native try-finally handler"); #endif + } + catch (const Js::JavascriptException& err) + { + pExceptionObject = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch(const Js::JavascriptException& err) - { - pExceptionObject = err.GetAndClear(); - } - if (pExceptionObject) { #if ENABLE_NATIVE_CODEGEN - if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) - { - if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); - } - } - else + if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) { - if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) - { - WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, framePtr); - } + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); } #endif // Clone static exception object early in case finally block overwrites it @@ -817,79 +829,91 @@ namespace Js { Js::JavascriptExceptionObject* pExceptionObject = NULL; void* continuationAddr = NULL; + void * tryHandlerAddrOfReturnAddr = nullptr; PROBE_STACK(scriptContext, Constants::MinStackJitEHBailout); - - try { - // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function - // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. - // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). - // In order to circumvent that we are throwing OutOfMemory. - if (!tryAddr) + void * addrOfReturnAddr = (void*)((char*)framePtr + sizeof(char*)); + Js::JavascriptExceptionOperators::TryHandlerAddrOfReturnAddrStack tryHandlerAddrOfReturnAddrStack(scriptContext, addrOfReturnAddr); + + try { - Assert(false); - ThrowOutOfMemory(scriptContext); - } + // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function + // that may throw. The current workaround is to add the following dummy throw to prevent this optimization. + // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below). + // In order to circumvent that we are throwing OutOfMemory. + if (!tryAddr) + { + Assert(false); + ThrowOutOfMemory(scriptContext); + } #ifdef _M_IX86 - 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 + 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 - // 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, save the current frame ptr, and adjust the frame to access - // locals in native code. - mov eax, tryAddr + // Set up the call target, save the current frame ptr, and adjust the frame to access + // locals in native code. + mov eax, tryAddr #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 - push ebp - mov ebp, framePtr - call eax - pop ebp + push ebp + mov ebp, framePtr + call eax + pop ebp - // The native code gives us the address where execution should continue on exit - // from the region. - mov continuationAddr, eax + // The native code gives us the address where execution should continue on exit + // from the region. + mov continuationAddr, 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 + } + catch (const Js::JavascriptException& err) + { + pExceptionObject = err.GetAndClear(); + tryHandlerAddrOfReturnAddr = scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr(); + } } - catch (const Js::JavascriptException& err) - { - pExceptionObject = err.GetAndClear(); - } - if (pExceptionObject) { // Clone static exception object early in case finally block overwrites it pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext); + +#if ENABLE_NATIVE_CODEGEN + if (pExceptionObject->GetExceptionContext() && pExceptionObject->GetExceptionContext()->ThrowingFunction()) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr /* start stackwalk from the current frame */, tryHandlerAddrOfReturnAddr); + } +#endif } void* newContinuationAddr = NULL; @@ -952,6 +976,10 @@ namespace Js if (pExceptionObject) { + if (scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr() != nullptr) + { + WalkStackForCleaningUpInlineeInfo(scriptContext, nullptr, scriptContext->GetThreadContext()->GetTryHandlerAddrOfReturnAddr()); + } JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext); }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-79xq-c7xr-29jhghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2018-8541ghsaADVISORY
- www.securityfocus.com/bid/105771mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1042107mitrevdb-entryx_refsource_SECTRACK
- github.com/chakra-core/ChakraCore/commit/3bee8f018e15c803d87d8b2981d0522f6d58ecacghsaWEB
- github.com/chakra-core/ChakraCore/pull/5827ghsaWEB
- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8541ghsax_refsource_CONFIRMWEB
- web.archive.org/web/20210124213905/http://www.securityfocus.com/bid/105771ghsaWEB
- web.archive.org/web/20211126224439/http://www.securitytracker.com/id/1042107ghsaWEB
News mentions
0No linked articles in our index yet.