ChakraCore icon indicating copy to clipboard operation
ChakraCore copied to clipboard

Throw a `FatalJsReentrancyError`

Open bin2415 opened this issue 4 years ago • 1 comments

PoC:

function main() {
function v0(v1,v2) {
    const v7 = new Int16Array(44915);
    for (let v10 = 0; v10 < 1337; v10++) {
        v7[v10] = 1337;
        v7[v10] = v2;
    }
}
const v12 = new Promise(v0);
}
main();

Backtrace:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x0000000102e9e4f1 libChakraCore.dylib`Js::Throw::FatalJsReentrancyError() at Throw.cpp:103:9
   100 	#if ENABLE_JS_REENTRANCY_CHECK
   101 	    void Throw::FatalJsReentrancyError()
   102 	    {
-> 103 	        AssertMsg(false, "Js reentrancy error!!");
   104 	        ReportFatalException(NULL, E_UNEXPECTED, Fatal_JsReentrancy_Error, 0);
   105 	    }
   106 	#endif
Target 0: (ch) stopped.
(lldb) bt 20
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  * frame #0: 0x0000000102e9e4f1 libChakraCore.dylib`Js::Throw::FatalJsReentrancyError() at Throw.cpp:103:9
    frame #1: 0x00000001030cb6d7 libChakraCore.dylib`ThreadContext::AssertJsReentrancy(this=0x0000000101808c58) at ThreadContext.h:1789:13
    frame #2: 0x0000000103c4d015 libChakraCore.dylib`Js::DynamicObject::CallToPrimitiveFunction(this=0x00007ffeefbf93b0)::$_0::operator()() const at DynamicType.cpp:464:24
    frame #3: 0x0000000103c4c506 libChakraCore.dylib`void* ThreadContext::ExecuteImplicitCall<Js::DynamicObject::CallToPrimitiveFunction(void*, int, void**, Js::ScriptContext*)::$_0>(this=0x0000000101808c58, function=0x0000000908227ac0, flags=ImplicitCall_ToPrimitive, implicitCall=(anonymous class) @ 0x00007ffeefbf93b0)::$_0) at ThreadContext.h:1593:30
    frame #4: 0x0000000103c4c1ed libChakraCore.dylib`Js::DynamicObject::CallToPrimitiveFunction(this=0x0000000908220360, toPrimitiveFunction=0x0000000908227ac0, propertyId=373, result=0x00007ffeefbf9530, requestContext=0x0000000101019858) at DynamicType.cpp:460:42
    frame #5: 0x0000000103c4bf73 libChakraCore.dylib`int Js::DynamicObject::ToPrimitiveImpl<373>(this=0x0000000908220360, result=0x00007ffeefbf9530, requestContext=0x0000000101019858) at DynamicType.cpp:451:16
    frame #6: 0x0000000103c4bce9 libChakraCore.dylib`Js::DynamicObject::ToPrimitive(this=0x0000000908220360, hint=HintNumber, result=0x00007ffeefbf9530, requestContext=0x0000000101019858) at DynamicType.cpp:398:20
    frame #7: 0x000000010363df98 libChakraCore.dylib`void* Js::JavascriptConversion::OrdinaryToPrimitive<(Js::JavascriptHint)2>(value=0x0000000908220360, requestContext=0x0000000101019858) at JavascriptConversion.cpp:532:21
    frame #8: 0x0000000103640592 libChakraCore.dylib`void* Js::JavascriptConversion::MethodCallToPrimitive<(Js::JavascriptHint)2>(value=0x0000000908220360, requestContext=0x0000000101019858) at JavascriptConversion.cpp:468:20
    frame #9: 0x00000001036399b7 libChakraCore.dylib`void* Js::JavascriptConversion::ToPrimitive<(Js::JavascriptHint)2>(aValue=0x0000000908220360, requestContext=0x0000000101019858) at JavascriptConversion.cpp:416:20
    frame #10: 0x000000010363b80c libChakraCore.dylib`Js::JavascriptConversion::ToUInt32_Full(aValue=0x0000000908220360, scriptContext=0x0000000101019858) at JavascriptConversion.cpp:1258:30
    frame #11: 0x0000000102cbcae4 libChakraCore.dylib`Js::JavascriptConversion::ToUInt32(aValue=0x0000000908220360, scriptContext=0x0000000101019858) at JavascriptConversion.inl:86:13
    frame #12: 0x0000000103670b85 libChakraCore.dylib`Js::JavascriptConversion::ToInt16(aValue=0x0000000908220360, scriptContext=0x0000000101019858) at JavascriptConversion.inl:195:20
    frame #13: 0x00000001036af407 libChakraCore.dylib`bool MemsetConversion<short, &(Js::JavascriptConversion::ToInt16(void*, Js::ScriptContext*))>(this=0x00007ffeefbf97e0)::'lambda'()::operator()() const at JavascriptOperators.cpp:5190:23
    frame #14: 0x00000001036af3a3 libChakraCore.dylib`Js::ImplicitCallFlags ThreadContext::TryWithDisabledImplicitCall<bool MemsetConversion<short, &(Js::JavascriptConversion::ToInt16(void*, Js::ScriptContext*))>(void*, Js::ScriptContext*, short*)::'lambda'()>(this=0x0000000101808c58, fn=(anonymous class) @ 0x00007ffeefbf97e0) at ThreadContext.h:370:9
    frame #15: 0x0000000103674a0a libChakraCore.dylib`bool MemsetConversion<short, &(Js::JavascriptConversion::ToInt16(void*, Js::ScriptContext*))>(value=0x0000000908220360, scriptContext=0x0000000101019858, result=0x00007ffeefbf99b2) at JavascriptOperators.cpp:5188:70
    frame #16: 0x0000000103673865 libChakraCore.dylib`Js::JavascriptOperators::OP_Memset(instance=0x0000000908227740, start=525, value=0x0000000908220360, length=812, scriptContext=0x0000000101019858) at JavascriptOperators.cpp:5220:9
    frame #17: 0x00000009082e016e
    frame #18: 0x000000010360cf17 libChakraCore.dylib`Js::InterpreterStackFrame::CallLoopBody(this=0x00007ffeefbfad90, address=(0x00000009082e0000))(Js::RecyclableObject*, Js::CallInfo, ...)) at InterpreterStackFrame.cpp:6358:13
    frame #19: 0x000000010360c574 libChakraCore.dylib`Js::InterpreterStackFrame::DoLoopBodyStart(this=0x00007ffeefbfad90, loopNumber=0, layoutSize=SmallLayout, doProfileLoopCheck=false, isFirstIteration=false) at InterpreterStackFrame.cpp:6162:35

How to reproduce:

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

bin2415 avatar Apr 11 '21 13:04 bin2415

This is an Abort in a release build but sev2 as not a new issue and highly unlikely to hit in real world code - it's triggered by setting a primitive value in a typed array AND then overwriting it with a non-primitive that has some kind of toPrimitive method within the same jitted loop.

Reduced repro:

function v0(v2) {
    var v7 = new Int16Array(2);
    for (var v10 = 0; v10 < 2; v10++) {
        v7[v10] = 0;
        v7[v10] = v2;
    }
}
v0(function(){});

Run with -forcejitloopbody -bgjit- to guarantee repro; to repro without flags needs higher loop count and not guaranteed - when bgjit is enabled (as it is by default) code execution can finish before loopbodyjit is done.

Problem is when jitting the loop body OP_Memset is used for both assigns, but this operation does not permit implicit calls so must not be used for the second assign.

rhuanjl avatar Apr 15 '21 09:04 rhuanjl