Throw a `FatalJsReentrancyError`
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
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.