chakra-core / ChakraCore

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

Crash due to Assertion in `GlobalOpt::CollectMemOpInfo` #6676

Open bin2415 opened 3 years ago

bin2415 commented 3 years ago

poc:

function main() {
for (let v3 = -2; v3 < 1337; v3 = v3 + 0) {
    const v4 = v3++;
}
}
main();

backtrace is:

(gdb) bt
#0  0x000055cd2ac3320a in GlobOpt::CollectMemOpInfo (this=0x7fb322aeefd0, instrBegin=<optimized out>, instr=<optimized out>, src1Val=0x7fb324fbd270, src2Val=<optimized out>) at /src/chakracore/lib/Backend/GlobOpt.cpp:2360
#1  0x000055cd2abf1ea0 in GlobOpt::OptInstr (this=0x7fb322aeefd0, instr=<optimized out>, isInstrRemoved=0x7fb322aedd20) at /src/chakracore/lib/Backend/GlobOpt.cpp:2739
#2  0x000055cd2abe17a7 in GlobOpt::OptBlock (this=<optimized out>, block=0x7fb324fb3508) at /src/chakracore/lib/Backend/GlobOpt.cpp:520
#3  0x000055cd2abdbcd7 in GlobOpt::ForwardPass (this=0x7fb322aeefd0) at /src/chakracore/lib/Backend/GlobOpt.cpp:401
#4  0x000055cd2abd8637 in GlobOpt::Optimize (this=<optimized out>) at /src/chakracore/lib/Backend/GlobOpt.cpp:211
#5  0x000055cd2ab831c7 in Func::TryCodegen (this=<optimized out>) at /src/chakracore/lib/Backend/Func.cpp:457
#6  0x000055cd2ab819f8 in Func::Codegen (alloc=0x7fb322af04a0, workItem=0x7fb324fa1030, threadContextInfo=0x623000000198, scriptContextInfo=0x622000000158, outputData=0x7fb322af0740, epInfo=0x7fb325165180, runtimeInfo=<optimized out>,
    polymorphicInlineCacheInfo=<optimized out>, codeGenAllocators=<optimized out>, codeGenProfiler=<optimized out>, isBackgroundJIT=<optimized out>) at /src/chakracore/lib/Backend/Func.cpp:325
#7  0x000055cd2a62990f in NativeCodeGenerator::CodeGen (this=<optimized out>, pageAllocator=<optimized out>, workItemData=0x612000000540, jitWriteData=..., foreground=false, epInfo=0x7fb325165180) at /src/chakracore/lib/Backend/NativeCodeGenerator.cpp:890
#8  0x000055cd2a62eaf8 in NativeCodeGenerator::CodeGen (this=0x6130000005d8, pageAllocator=<optimized out>, workItem=<optimized out>, foreground=64) at /src/chakracore/lib/Backend/NativeCodeGenerator.cpp:1007
#9  0x000055cd2a637e80 in NativeCodeGenerator::Process (this=<optimized out>, job=<optimized out>, threadData=0x615000002658) at /src/chakracore/lib/Backend/NativeCodeGenerator.cpp:1907
#10 0x000055cd2a77bad4 in JsUtil::BackgroundJobProcessor::Process (this=<optimized out>, job=<optimized out>, threadData=0x615000002658) at /src/chakracore/lib/Common/Common/Jobs.cpp:1037
#11 0x000055cd2a77c2ad in JsUtil::BackgroundJobProcessor::Run (this=0x612000000098, threadData=<optimized out>) at /src/chakracore/lib/Common/Common/Jobs.cpp:1135
#12 0x000055cd2a7740d0 in JsUtil::BackgroundJobProcessor::StaticThreadProc (lpParam=<optimized out>) at /src/chakracore/lib/Common/Common/Jobs.cpp:1319
#13 0x000055cd26fc3adb in CorUnix::CPalThread::ThreadEntry (pvParam=0x61c000001880) at /src/chakracore/pal/src/thread/pal_thread.cpp:1605
#14 0x000055cd26e2df3f in __asan::AsanThread::ThreadStart(unsigned long, __sanitizer::atomic_uintptr_t*) ()
#15 0x00007fb32a3986db in start_thread (arg=0x7fb322af1700) at pthread_create.c:463
#16 0x00007fb32990771f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

The source code of the assertion is:

// If an instr where the dst is an induction variable (and thus is being written to) is not caught by a case in the above
        // switch statement (which implies that this instr does not contributes to a inductionVariableChangeInfo) and in the default
        // case does not set doMemOp to false (which implies that this instr does not invalidate this MemOp), then FailFast as we
        // should not be performing a MemOp under these conditions. 
        AssertOrFailFast(!instr->GetDst() || instr->m_opcode == Js::OpCode::IncrLoopBodyCount || !loop->memOpInfo ||

            // Refer to "Case #2" described above in this function. For the following IR:
            // Line #1: s4(s2) = Add_I4 s3(s1) 1
            // Line #2: s3(s1) = Ld_A   s4(s2)
            // do not consider line #2 as a violating instr
            (instr->m_opcode == Js::OpCode::Ld_I4 &&
                prevInstr && (prevInstr->m_opcode == Js::OpCode::Add_I4 || prevInstr->m_opcode == Js::OpCode::Sub_I4) &&
                instr->GetSrc1()->IsRegOpnd() &&
                instr->GetDst()->IsRegOpnd() &&
                prevInstr->GetDst()->IsRegOpnd() &&
                instr->GetDst()->GetStackSym() == prevInstr->GetSrc1()->GetStackSym() &&
                instr->GetSrc1()->GetStackSym() == prevInstr->GetDst()->GetStackSym()) ||

            !loop->memOpInfo->inductionVariableChangeInfoMap->ContainsKey(GetVarSymID(instr->GetDst()->GetStackSym())));
rhuanjl commented 3 years ago

This is a very strange bug - an AssertOrFailFast should catch abort in a release build BUT this doesn't.

It only hits in a Debug build - this is an incorrect path being followed in Debug builds only. In a release build we follow a different path and never get to this line.

So, going to have to look at what's different between Release and Debug builds when running this snippet.