Bug 281960
Summary: | [WASM] JavaScriptCore Assertion Failed in JSC::Wasm::LLIntGenerator::checkConsistency() | ||
---|---|---|---|
Product: | WebKit | Reporter: | xiangwei1895 |
Component: | WebAssembly | Assignee: | Nobody <webkit-unassigned> |
Status: | RESOLVED DUPLICATE | ||
Severity: | Normal | CC: | yijia_huang |
Priority: | P2 | ||
Version: | WebKit Nightly Build | ||
Hardware: | Unspecified | ||
OS: | Unspecified |
xiangwei1895
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 | ||
---|---|---|
Add attachment proposed patch, testcase, etc. |
Yijia Huang
*** This bug has been marked as a duplicate of bug 281934 ***