Bug 281960

Summary: [WASM] JavaScriptCore Assertion Failed in JSC::Wasm::LLIntGenerator::checkConsistency()
Product: WebKit Reporter: xiangwei1895
Component: WebAssemblyAssignee: Nobody <webkit-unassigned>
Status: RESOLVED DUPLICATE    
Severity: Normal CC: yijia_huang
Priority: P2    
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   

xiangwei1895
Reported 2024-10-23 02:25:01 PDT
JavaScriptCore Assertion Failed in JSC::Wasm::LLIntGenerator::checkConsistency() I. Summary The following sample causes jsc to have an assertion failed when compiling the wasm module. This problem exists in the latest jsc, compiled in debug mode (this assertion is ignored in release mode). PoC: ----------------------------------- load("wasm-module-builder.js"); const builder = new WasmModuleBuilder(); let $sig3 = builder.addType(kSig_i_iii); let emptyFunc = builder.addFunction("emptyFunc", kSig_v_v) .addBody([]) .exportFunc(); let main17 = builder.addFunction(undefined, $sig3).exportAs('main'); main17.addBody([ ...wasmI32Const(1337), ...wasmI32Const(1337), kExprI32Eq, kExprRefFunc, emptyFunc.index, kExprBrOnNull, 0, kExprDrop, ]); const instance = builder.instantiate(); print(instance.exports.main(1, 2, 3)); ----------------------------------- Backtrace: ----------------------------------- ASSERTION FAILED: expression == slot || expression.isConstant() || expression.isArgument() || static_cast<unsigned>(expression.toLocal()) < m_codeBlock->m_numVars /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp(546) : auto JSC::Wasm::LLIntGenerator::checkConsistency()::(anonymous class)::operator()(JSC::VirtualRegister, JSC::VirtualRegister) const (gdb) bt #0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=140735954261568) at ./nptl/pthread_kill.c:44 #1 __pthread_kill_internal (signo=6, threadid=140735954261568) at ./nptl/pthread_kill.c:78 #2 __GI___pthread_kill (threadid=140735954261568, signo=signo@entry=6) at ./nptl/pthread_kill.c:89 #3 0x00007ffff5949476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26 #4 0x00007ffff592f7f3 in __GI_abort () at ./stdlib/abort.c:79 #5 0x000055555560804b in WTFCrashWithInfo () at /data/workspace/WebKit/wasmdebug/JSCOnly/Debug/WTF/Headers/wtf/Assertions.h:912 #6 0x00005555571433ea in JSC::Wasm::LLIntGenerator::checkConsistency()::{lambda(JSC::VirtualRegister, JSC::VirtualRegister)#2}::operator()(JSC::VirtualRegister, JSC::VirtualRegister) const (this=0x7fffa48e61a8, expression=..., slot=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:546 #7 0x00005555571432d6 in JSC::Wasm::LLIntGenerator::walkExpressionStack<WTF::Vector<JSC::Wasm::FunctionParserTypes<JSC::Wasm::LLIntGenerator::ControlType, JSC::VirtualRegister, JSC::CallLinkInfoBase::CallType>::TypedExpression, 16ul, WTF::UnsafeVectorOverflow, 16ul, WTF::FastMalloc>, JSC::Wasm::LLIntGenerator::checkConsistency()::{lambda(JSC::VirtualRegister, JSC::VirtualRegister)#2}>(WTF::Vector<JSC::Wasm::FunctionParserTypes<JSC::Wasm::LLIntGenerator::ControlType, JSC::VirtualRegister, JSC::CallLinkInfoBase::CallType>::TypedExpression, 16ul, WTF::UnsafeVectorOverflow, 16ul, WTF::FastMalloc>&, unsigned int, JSC::Wasm::LLIntGenerator::checkConsistency()::{lambda(JSC::VirtualRegister, JSC::VirtualRegister)#2} const&) ( this=0x7fffa48f8720, expressionStack=..., stackSize=20, functor=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:513 #8 0x00005555571430f3 in JSC::Wasm::LLIntGenerator::walkExpressionStack<WTF::Vector<JSC::Wasm::FunctionParserTypes<JSC::Wasm::LLIntGenerator::ControlType, JSC::VirtualRegister, JSC::CallLinkInfoBase::CallType>::TypedExpression, 16ul, WTF::UnsafeVectorOverflow, 16ul, WTF::FastMalloc>, JSC::Wasm::LLIntGenerator::checkConsistency()::{lambda(JSC::VirtualRegister, JSC::VirtualRegister)#2}>(WTF::Vector<JSC::Wasm::FunctionParserTypes<JSC::Wasm::LLIntGenerator::ControlType, JSC::VirtualRegister, JSC::CallLinkInfoBase::CallType>::TypedExpression, 16ul, WTF::UnsafeVectorOverflow, 16ul, WTF::FastMalloc>&, JSC::Wasm::LLIntGenerator::checkConsistency()::{lambda(JSC::VirtualRegister, JSC::VirtualRegister)#2} const&) (this=0x7fffa48f8720, expressionStack=..., functor=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:520 #9 0x000055555712ba26 in JSC::Wasm::LLIntGenerator::checkConsistency (this=0x7fffa48f8720) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:545 #10 0x0000555557124639 in JSC::Wasm::LLIntGenerator::push (this=0x7fffa48f8720) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:283 #11 0x000055555713055b in JSC::Wasm::LLIntGenerator::addBranchNull (this=0x7fffa48f8720, data=..., reference=..., returnValues=..., shouldNegate=false, result=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:1432 #12 0x00005555571de829 in JSC::Wasm::FunctionParser<JSC::Wasm::LLIntGenerator>::parseExpression (this=0x7fffa48f4d70) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmFunctionParser.h:2918 #13 0x00005555571b8b73 in JSC::Wasm::FunctionParser<JSC::Wasm::LLIntGenerator>::parseBody (this=0x7fffa48f4d70) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmFunctionParser.h:525 #14 0x000055555712382f in JSC::Wasm::FunctionParser<JSC::Wasm::LLIntGenerator>::parse (this=0x7fffa48f4d70) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmFunctionParser.h:478 #15 0x0000555557122a39 in JSC::Wasm::parseAndCompileBytecode (function=std::span of length 14 = {...}, signature=..., info=..., functionIndex=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:634 #16 0x000055555713a892 in JSC::Wasm::LLIntPlan::compileFunction (this=0x7fffec048210, functionIndex=...) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntPlan.cpp:103 #17 0x000055555708566d in JSC::Wasm::EntryPlan::compileFunctions (this=0x7fffec048210, effort=JSC::Wasm::Plan::Partial) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmEntryPlan.cpp:218 #18 0x000055555713c309 in JSC::Wasm::LLIntPlan::work (this=0x7fffec048210, effort=JSC::Wasm::Plan::Partial) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmLLIntPlan.cpp:247 #19 0x000055555743102a in JSC::Wasm::Worklist::Thread::work (this=0x7fffec0305e0) at /data/workspace/WebKit/Source/JavaScriptCore/wasm/WasmWorklist.cpp:108 #20 0x0000555557c10f0b in WTF::AutomaticThread::start(WTF::AbstractLocker const&)::$_0::operator()() const (this=0x7fffec145d68) at /data/workspace/WebKit/Source/WTF/wtf/AutomaticThread.cpp:225 #21 0x0000555557c10ba9 in WTF::Detail::CallableWrapper<WTF::AutomaticThread::start(WTF::AbstractLocker const&)::$_0, void>::call() (this=0x7fffec145d60) at /data/workspace/WebKit/Source/WTF/wtf/Function.h:53 #22 0x0000555555f63a97 in WTF::Function<void ()>::operator()() const (this=0x7fffa48f8e20) at /data/workspace/WebKit/Source/WTF/wtf/Function.h:82 #23 0x0000555557def879 in WTF::Thread::entryPoint (newThreadContext=0x7fffec1128e0) at /data/workspace/WebKit/Source/WTF/wtf/Threading.cpp:266 #24 0x0000555557e643d5 in WTF::wtfThreadEntryPoint (context=0x7fffec1128e0) at /data/workspace/WebKit/Source/WTF/wtf/posix/ThreadingPOSIX.cpp:239 #25 0x00007ffff599bac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442 #26 0x00007ffff5a2d850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 ----------------------------------- II. Root cause analysis The inconsistency between `expression` and `slot` is mainly due to the asynchrony between `m_stackSize` and `expression_stack`. Reason for asynchrony: When `shouldNegate` is false, the `addBranchNull`, two `push()` are performed consecutively in `addBranchNull`. The `push()` causes m_stackSize to increase, but `expression_stack` is not updated. This causes `checkConsistency()` to report assertion failed during the second push. ``` void checkConsistency() { #if ASSERT_ENABLED // The rules for locals and constants in the stack are: // 1) Locals have to be materialized whenever a control entry is pushed to the control stack (i.e. every time we splitStack) // NOTE: This is a trade-off so that set_local does not have to walk up the control stack looking for delayed get_locals // 2) If the control entry is a loop, we also need to materialize constants in the newStack, since those slots will be written // to from loop back edges // 3) Both locals and constants have to be materialized before branches, since multiple branches might share the same target, // we can't make any assumptions about the stack state at that point, so we materialize the stack. for (ControlEntry& controlEntry : m_parser->controlStack()) { walkExpressionStack(controlEntry, [&](VirtualRegister expression, VirtualRegister slot) { ASSERT(expression == slot || expression.isConstant()); }); } walkExpressionStack(m_parser->expressionStack(), [&](VirtualRegister expression, VirtualRegister slot) { ASSERT(expression == slot || expression.isConstant() || expression.isArgument() || static_cast<unsigned>(expression.toLocal()) < m_codeBlock->m_numVars); }); #endif // ASSERT_ENABLED } auto LLIntGenerator::addBranchNull(ControlType& data, ExpressionType reference, Stack& returnValues, bool shouldNegate, ExpressionType& result) -> PartialResult { // Leave a hole for the reference and avoid overwriting it with the condition. if (!shouldNegate) push(); auto condition = push(); } enum NoConsistencyCheckTag { NoConsistencyCheck }; ExpressionType push(NoConsistencyCheckTag) { m_maxStackSize = std::max(m_maxStackSize, ++m_stackSize); return virtualRegisterForLocal(m_stackSize - 1); } ExpressionType push() { checkConsistency(); return push(NoConsistencyCheck); } ``` This bug creates a mismatch between the expression stack and slot allocations in the WebAssembly compiler, triggering assertion failures during consistency checks. Although currently not directly exploitable, this discrepancy compromises the compiler's internal state coherence and poses potential security risks. It potentially opens avenues for memory corruption, type confusion, and other vulnerabilities. The flaw could lead to incorrect code generation and runtime errors, and might serve as a stepping stone for more severe exploits when combined with other issues or future changes. If possible, I would like you to assign a cve id to this issue. III. Reproduce JavaScript version: b4239a38c109a1f5980fc9c1cfe9a98cd3328741 Build platform: Ubuntu 22.04.2 LTS Build Command: ./Tools/Scripts/build-jsc --debug --jsc-only Execution Command: ./jsc poc.js
Attachments
Yijia Huang
Comment 1 2024-10-23 10:36:33 PDT
*** This bug has been marked as a duplicate of bug 281934 ***
Note You need to log in before you can comment on or make changes to this bug.