chakra-core / ChakraCore

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

Invalid memory access in `GetEntryPoint` #6691

Open bin2415 opened 3 years ago

bin2415 commented 3 years ago

PoC:

function main() {
const v0 = {};
async function v1(v2,v3,v4,v5,v6) {
    function v7(v8) {
    }
    const v9 = v7;
    for (const v10 in v0) {
        const v12 = await 320832.93626390724;
    }
    const v13 = v1();
    let v15 = 1337;
    for (let v17 = 0; v17 < v15; v17 = v17 && 350074.8305491025) {
        v15 = v17;
    }
    const v19 = new Promise(v9);
}
const v20 = v1();
}
main();

Backtrace:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x841f0f66db)
    frame #0: 0x00000001026b72ec libChakraCore.dylib`Js::Type::GetEntryPoint(this=0x000000841f0f66c3) const at Type.h:51:57
   48           inline TypeId GetTypeId() const { return typeId; }
   49           void SetTypeId(TypeId typeId) { this->typeId = typeId; }
   50           RecyclableObject* GetPrototype() const { return prototype; }
-> 51           JavascriptMethod GetEntryPoint() const { return entryPoint; }
   52           JavascriptLibrary* GetLibrary() const { return javascriptLibrary; }
   53           ScriptContext * GetScriptContext() const;
   54           Recycler * GetRecycler() const;
Target 0: (ch) stopped.
(lldb) bt 20
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x841f0f66db)
  * frame #0: 0x00000001026b72ec libChakraCore.dylib`Js::Type::GetEntryPoint(this=0x000000841f0f66c3) const at Type.h:51:57
    frame #1: 0x0000000103686e3d libChakraCore.dylib`Js::RecyclableObject::GetEntryPoint(this=0x0000000102206f0e) const at RecyclableObject.cpp:175:33
    frame #2: 0x0000000103036ee4 libChakraCore.dylib`Js::JavascriptConversion::IsCallable(aValue=0x0000000102206f0e) at JavascriptConversion.cpp:41:47
    frame #3: 0x0000000103036e47 libChakraCore.dylib`Js::JavascriptConversion::IsCallable(aValue=0x0000000102206f0e) at JavascriptConversion.cpp:35:16
    frame #4: 0x0000000103480b95 libChakraCore.dylib`Js::JavascriptPromise::NewInstance(function=0x00000009078f0100, callInfo=(Count = 2, Flags = CallFlags_New, unused = 0)) at JavascriptPromise.cpp:46:37
    frame #5: 0x0000000907c01679
    frame #6: 0x000000010360d15e libChakraCore.dylib`amd64_CallFunction at JavascriptFunctionA.S:100
    frame #7: 0x00000001032c62db libChakraCore.dylib`void* Js::JavascriptFunction::CallFunction<true>(function=0x00000001020e67d0, entryPoint=(0x0000000907c01000), args=Arguments @ 0x00007ffeef40df48, useLargeArgCount=false)(Js::RecyclableObject*, Js::CallInfo, ...), Js::Arguments, bool) at JavascriptFunction.cpp:1364:16
    frame #8: 0x00000001032d2a47 libChakraCore.dylib`Js::JavascriptGenerator::CallGenerator(this=0x0000000909434960, data=0x00000001020e5030, resumeKind=Normal) at JavascriptGenerator.cpp:185:26
    frame #9: 0x0000000103283904 libChakraCore.dylib`Js::JavascriptAsyncFunction::EntryAsyncSpawnStepNextFunction(function=0x000000090943b460, callInfo=(Count = 1, Flags = CallFlags_Value, unused = 0)) at JavascriptAsyncFunction.cpp:93:31
    frame #10: 0x0000000103283ae8 libChakraCore.dylib`Js::JavascriptAsyncFunction::AsyncSpawnStep(stepFunction=0x000000090943b460, generator=0x0000000909434960, resolve=0x000000090943d4e0, reject=0x000000090943d540) at JavascriptAsyncFunction.cpp:151:25
    frame #11: 0x00000001032837b6 libChakraCore.dylib`Js::JavascriptAsyncFunction::BeginAsyncFunctionExecution(generator=0x0000000909434960) at JavascriptAsyncFunction.cpp:73:9
    frame #12: 0x00000001032835a3 libChakraCore.dylib`Js::JavascriptAsyncFunction::EntryAsyncFunctionImplementation(function=0x0000000907b27880, callInfo=(Count = 1, Flags = CallFlags_Value, unused = 0)) at JavascriptAsyncFunction.cpp:52:12
    frame #13: 0x0000000907c0151d
    frame #14: 0x000000010360d15e libChakraCore.dylib`amd64_CallFunction at JavascriptFunctionA.S:100
    frame #15: 0x00000001032c62db libChakraCore.dylib`void* Js::JavascriptFunction::CallFunction<true>(function=0x00000001020e67d0, entryPoint=(0x0000000907c01000), args=Arguments @ 0x00007ffeef40e5d8, useLargeArgCount=false)(Js::RecyclableObject*, Js::CallInfo, ...), Js::Arguments, bool) at JavascriptFunction.cpp:1364:16
    frame #16: 0x00000001032d2a47 libChakraCore.dylib`Js::JavascriptGenerator::CallGenerator(this=0x0000000909434900, data=0x00000001020e5030, resumeKind=Normal) at JavascriptGenerator.cpp:185:26
    frame #17: 0x0000000103283904 libChakraCore.dylib`Js::JavascriptAsyncFunction::EntryAsyncSpawnStepNextFunction(function=0x000000090943b3f0, callInfo=(Count = 1, Flags = CallFlags_Value, unused = 0)) at JavascriptAsyncFunction.cpp:93:31
    frame #18: 0x0000000103283ae8 libChakraCore.dylib`Js::JavascriptAsyncFunction::AsyncSpawnStep(stepFunction=0x000000090943b3f0, generator=0x0000000909434900, resolve=0x000000090943d420, reject=0x000000090943d480) at JavascriptAsyncFunction.cpp:151:25
    frame #19: 0x00000001032837b6 libChakraCore.dylib`Js::JavascriptAsyncFunction::BeginAsyncFunctionExecution(generator=0x0000000909434900) at JavascriptAsyncFunction.cpp:73:9

How to reproduce it:

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

Its backtrace is similar with #6690. I am not sure if their root cause are same.

rhuanjl commented 3 years ago

Hmm - this is a different issue to the ScopeArray points I've been looking at - will investigate seperately.

rhuanjl commented 3 years ago

Simplified repro:

{
    let i = 0;
    function* v1() {
        function v7() {}
        for (var v10 in []) {
            yield undefined;
        }
        if(++i < 4)
            v1().next()
        v7 + 1;
    }
}
v1().next();

Use flags -mic:1 -off:simplejit -bgjit-

Note - in the original repro code runs fine until stack overflow occurs - then after top function throws 2nd function down tries to execute to run to completion - error is seen in fulljitted code of the whole function.

Simplified repro triggers the continuation with a control variable instead of waiting for stack overflow - flags force the fulljit to occur sooner without needing 50+ iterations. (or 3000+ to get stack overflow)

Problem is to do with lifetime of the function v7 across a for...in loop containing a yield (or await if using an async function) that is never invoked - slightly obscure; but another one we need to fix if we're keeping generator jit on.