-
Notifications
You must be signed in to change notification settings - Fork 830
Description
I ran an adapted version of linking0.wast (removing some assertions in the middle that aren't needed) from the current main with ./binaryen/bin/wasm-shell test/spec/testsuite/linking0.wast:
(module $Mt
(type (func (result i32)))
(type (func))
(table (export "tab") 10 funcref)
(elem (i32.const 2) $g $g $g $g)
(func $g (result i32) (i32.const 4))
(func (export "h") (result i32) (i32.const -4))
(func (export "call") (param i32) (result i32)
(call_indirect (type 0) (local.get 0))
)
)
(register "Mt" $Mt)
(assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
(func $f (result i32) (i32.const 0))
(elem (i32.const 7) $f)
(memory 0)
(memory $m 1)
(memory 0)
(data $m (i32.const 0x10000) "d") ;; out of bounds
)
"out of bounds memory access"
)
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))Running this gives a segfault during the last assert_return statement: backtrace.
This test is basically testing that a partially-instantiated module that fails to instantiate can still mutate an imported table. If we remove the error that causes it to fail to instantiate, then the test passes:
(module $Mt
(type (func (result i32)))
(type (func))
(table (export "tab") 10 funcref)
(elem (i32.const 2) $g $g $g $g)
(func $g (result i32) (i32.const 4))
(func (export "h") (result i32) (i32.const -4))
(func (export "call") (param i32) (result i32)
(call_indirect (type 0) (local.get 0))
)
)
(register "Mt" $Mt)
;; Now instantiation completes successfully
;; (assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
(func $f (result i32) (i32.const 0))
(elem (i32.const 7) $f)
(memory 0)
(memory $m 1)
(memory 0)
;; (data $m (i32.const 0x10000) "d") ;; out of bounds
)
;; "out of bounds memory access"
;; )
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))I suspect that when the module instantiation fails, some memory gets freed that is still referenced in the table of the imported function which causes the segfault. Strangely though, the backtrace doesn't confirm this. Instead the backtrace seems to show that getCurrContinuationOrNull() is returning a garbage value (continuations is empty but we return continuations.back()):
(lldb) f 6
frame #6: 0x0000555555657a57 wasm-shell`wasm::ExpressionRunner<wasm::ModuleRunner>::getCurrContinuationOrNull(this=0x0000555555756900) at wasm-interpreter.h:451:50
448 if (!continuationStore || continuationStore->continuations.empty()) {
449 return {};
450 }
-> 451 return continuationStore->continuations.back();
452 }
453
454 std::shared_ptr<ContData> getCurrContinuation() {
(lldb) p continuationStore->continuations
(std::vector<std::shared_ptr<wasm::ContData> >) size=0 {}
(lldb) p !continuationStore || continuationStore->continuations.empty()
(bool) falseBased on this frame, it seems like continuationStore was mutated by another thread in between the check and returning continuations.back(), but I'm not sure if we even have concurrent threads here and this doesn't make as much sense in context of the test.