chakra-core / ChakraCore

ChakraCore is an open source Javascript engine with a C API.
MIT License
9.1k stars 1.19k forks source link

Segmentation Fault in `Js::ScopeSlots::GetCount` #6678

Closed bin2415 closed 3 years ago

bin2415 commented 3 years ago

PoC:

function main() {
async function v0(v1,v2,v3,v4) {
    for (let v8 = 0; v8 < 1337; v8++) {
        const v9 = class V9 {
            constructor(v11,v12,v13) {
                const v14 = !v8;
            }
            isView(v16,...v17) {
            }
            atan(v19) {
            }
            isFinite(v21,v22,v23) {
            }
        };
        const v24 = await v2;
    }
}
const v25 = v0();
CollectGarbage();
}
main();

backtrace:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
  * frame #0: 0x00000001032d02dd libChakraCore.dylib`Js::ScopeSlots::GetCount(this=0x00007ffeefbfe058) const at FunctionBody.h:3513:66
    frame #1: 0x0000000103683263 libChakraCore.dylib`Js::JavascriptOperators::OP_CloneScopeSlots(slotArray=0x0000000000000000, scriptContext=0x0000000908808258) at JavascriptOperators.cpp:7681:74
    frame #2: 0x000000090832044d
    frame #3: 0x0000000103c0d15e libChakraCore.dylib`amd64_CallFunction at JavascriptFunctionA.S:100
    frame #4: 0x00000001038c62db libChakraCore.dylib`void* Js::JavascriptFunction::CallFunction<true>(function=0x00000001007e67d0, entryPoint=(libChakraCore.dylib`NativeCodeGenerator::CheckCodeGenThunk(Js::RecyclableObject*, Js::CallInfo, ...)), args=Arguments @ 0x00007ffeefbfe238, useLargeArgCount=false)(Js::RecyclableObject*, Js::CallInfo, ...), Js::Arguments, bool) at JavascriptFunction.cpp:1364:16
    frame #5: 0x00000001038d2a47 libChakraCore.dylib`Js::JavascriptGenerator::CallGenerator(this=0x0000000908249060, data=0x00000001007e5030, resumeKind=Normal) at JavascriptGenerator.cpp:185:26
    frame #6: 0x0000000103883904 libChakraCore.dylib`Js::JavascriptAsyncFunction::EntryAsyncSpawnStepNextFunction(function=0x00000009082d2850, callInfo=(Count = 1, Flags = CallFlags_Value, unused = 0)) at JavascriptAsyncFunction.cpp:93:31
    frame #7: 0x0000000103883ae8 libChakraCore.dylib`Js::JavascriptAsyncFunction::AsyncSpawnStep(stepFunction=0x00000009082d2850, generator=0x0000000908249060, resolve=0x00000009082402a0, reject=0x0000000908240300) at JavascriptAsyncFunction.cpp:151:25
    frame #8: 0x0000000103884577 libChakraCore.dylib`Js::JavascriptAsyncFunction::EntryAsyncSpawnCallStepFunction(function=0x00000009082d2770, callInfo=(Count = 2, Flags = CallFlags_Value, unused = 0)) at JavascriptAsyncFunction.cpp:130:5
    frame #9: 0x0000000103a873e6 libChakraCore.dylib`Js::JavascriptPromise::EntryReactionTaskFunction(function=0x00000009082d70a0, callInfo=(Count = 1, Flags = CallFlags_None, unused = 0)) at JavascriptPromise.cpp:1074:37
    frame #10: 0x0000000103c0d15e libChakraCore.dylib`amd64_CallFunction at JavascriptFunctionA.S:100
    frame #11: 0x00000001038c62db libChakraCore.dylib`void* Js::JavascriptFunction::CallFunction<true>(function=0x00000009082d70a0, entryPoint=(libChakraCore.dylib`Js::JavascriptPromise::EntryReactionTaskFunction(Js::RecyclableObject*, Js::CallInfo, ...) at JavascriptPromise.cpp:1037), args=Arguments @ 0x00007ffeefbfe9f0, useLargeArgCount=false)(Js::RecyclableObject*, Js::CallInfo, ...), Js::Arguments, bool) at JavascriptFunction.cpp:1364:16
    frame #12: 0x00000001038c65ff libChakraCore.dylib`Js::JavascriptFunction::CallRootFunctionInternal(obj=0x00000009082d70a0, args=Arguments @ 0x00007ffeefbfea60, scriptContext=0x0000000908808258, inScript=true) at JavascriptFunction.cpp:772:24
    frame #13: 0x00000001038c643c libChakraCore.dylib`Js::JavascriptFunction::CallRootFunction(obj=0x00000009082d70a0, args=<unavailable>, scriptContext=0x0000000908808258, inScript=true) at JavascriptFunction.cpp:717:15
    frame #14: 0x00000001038c63e1 libChakraCore.dylib`Js::JavascriptFunction::CallRootFunction(this=0x00000009082d70a0, args=<unavailable>, scriptContext=0x0000000908808258, inScript=true) at JavascriptFunction.cpp:832:16
    frame #15: 0x0000000102928525 libChakraCore.dylib`JsCallFunction::$_67::operator(this=0x00007ffeefbfeed0, scriptContext=0x0000000908808258, _actionEntryPopper=0x00007ffeefbfeeb0)(Js::ScriptContext*, TTD::TTDJsRTActionResultAutoRecorder&) const at Jsrt.cpp:2842:41
    frame #16: 0x0000000102927eb4 libChakraCore.dylib`_JsErrorCode ContextAPIWrapper<false, JsCallFunction::$_67>(this=0x00007ffeefbfee68, scriptContext=0x0000000908808258)::'lambda'(Js::ScriptContext*)::operator()(Js::ScriptContext*) const at JsrtInternal.h:237:16
    frame #17: 0x0000000102927884 libChakraCore.dylib`_JsErrorCode ContextAPIWrapper_Core<false, _JsErrorCode ContextAPIWrapper<false, JsCallFunction::$_67>(JsCallFunction::$_67)::'lambda'(Js::ScriptContext*)>(fn=(anonymous class) @ 0x00007ffeefbfee68) at JsrtInternal.h:192:23
    frame #18: 0x00000001028e3d46 libChakraCore.dylib`_JsErrorCode ContextAPIWrapper<false, JsCallFunction::$_67>(fn=(anonymous class) @ 0x00007ffeefbfeed0) at JsrtInternal.h:235:27
    frame #19: 0x00000001028e3d09 libChakraCore.dylib`::JsCallFunction(function=0x00000009082d70a0, args=0x00007ffeefbfefa0, cargs=1, result=0x00007ffeefbfef98) at Jsrt.cpp:2804:12
    frame #20: 0x000000010000bdeb ch`ChakraRTInterface::JsCallFunction(function=0x00000009082d70a0, arguments=0x00007ffeefbfefa0, argumentCount=1, result=0x00007ffeefbfef98) at ChakraRtInterface.h:416:149
    frame #21: 0x000000010001b35a ch`WScriptJsrt::CallbackMessage::CallFunction(this=0x0000000100307b70, fileName="/Users/cpang/Desktop/graduate/javascript/JS_Fuzzing/chakracore/crash2/Poc.js") at WScriptJsrt.cpp:1988:21
    frame #22: 0x000000010001b23d ch`WScriptJsrt::CallbackMessage::Call(this=0x0000000100307b70, fileName="/Users/cpang/Desktop/graduate/javascript/JS_Fuzzing/chakracore/crash2/Poc.js") at WScriptJsrt.cpp:1959:12
    frame #23: 0x000000010000543e ch`MessageQueue::ProcessAll(this=0x000000010770ae80, fileName="/Users/cpang/Desktop/graduate/javascript/JS_Fuzzing/chakracore/crash2/Poc.js") at MessageQueue.h:256:18

How to reproduce:

- ./build.sh -d -j
- ch -collectGarbage ./poc.js

The error code:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
    frame #0: 0x00000001042d02dd libChakraCore.dylib`Js::ScopeSlots::GetCount(this=0x00007ffeefbfe058) const at FunctionBody.h:3513:66
   3510
   3511         size_t GetCount() const
   3512         {
-> 3513             return ::Math::PointerCastToIntegralTruncate<size_t>(slotArray[EncodedSlotCountSlotIndex]);
   3514         }
   3515
   3516         void SetCount(uint count)
MadProbe commented 3 years ago

FYI, this issue isn't reproducable on windows 10 in interpreted and dynapogo modes.

bin2415 commented 3 years ago

It is reproducable on Ubuntu 18.04 and Mac OS 11.2 when built with debug version.

ppenzin commented 3 years ago

This is due to Js::JavascriptOperators::OP_CloneScopeSlots getting slotArray == nullptr.

I can reproduce the bug in Ubuntu 18 (WSL). However CollectGarbage (both the call and engine switch) is not needed, also the reproducer can be reduced to this:

function main() {
async function v0(v2) {
    for (let v8 = 0; v8 < 1337; v8++) {
        const v9 = class V9 {
            constructor(v11,v12,v13) {
                const v14 = !v8;
            }
        };
        const v24 = await v2;
    }
}
const v25 = v0();
}
main();

Command would then become ./ch poc.js.

rhuanjl commented 3 years ago

This is a new bug from jitting generators, will need to fix this or re-disable generator jit :( before release.

rhuanjl commented 3 years ago

I think it's to do with the handling of OpCode::CloneInnerScopeSlots in IRBuilder.cpp - hopefully will be able to write a fix this weekend.

rhuanjl commented 3 years ago

Reduced further:

async function v0() {
    for (let v8 = 0; v8 < 1337; v8++) {
        function foo () {v8}
        await undefined;
    }
}
v0();

The problem is v8 not being restored properly after await - only happens if v8 is declared with let AND referenced in a different inner scope (hence the function def - I swapped out the class definition to simplify)

I'm working on a fix.