Skip to content

Memory crash in eh::gcc::throw via chained catch_ref/throw_ref rethrow #6795

Description

@phihuthan

Describe the bug

A bounded-depth recursive chain where every unwinding frame catches an exception via catch_ref and immediately re-raises it via throw_ref causes a stack-buffer-underflow in sigaltstack, reached through Wasmer's unwinder (eh::gcc::throw_Unwind_RaiseException)

wasmer -vV; rustc -vV

wasmer 7.2.0-alpha.1
binary: wasmer-cli
commit-hash: 89ce1989711550e0212592943defae9500b4e2a7
commit-date: 2026-07-06
host: x86_64-unknown-linux-gnu
CPU flags: sse2 sse3 ssse3 sse4.1 sse4.2 popcnt avx bmi bmi2 avx2 avx512dq avx512vl avx512f lzcnt fma
runtimes: Singlepass, Cranelift, LLVM
features: wasix
rustc 1.99.0-nightly (3659db0d3 2026-07-05)
binary: rustc
commit-hash: 3659db0d3e2cd634c766fcda79ed118eca31a9fd
commit-date: 2026-07-05
host: x86_64-unknown-linux-gnu
release: 1.99.0-nightly
LLVM version: 22.1.8

Steps to reproduce

Testcase:

(module
  (type $i32_exnref (func (result i32 exnref)))  (tag $e-depth (param i32))
  (export "main" (func $main))

  (func $recurse (param $n i32)
    local.get $n  i32.eqz
    if  i32.const 0  throw $e-depth  end
    block $h (type $i32_exnref) (result i32 exnref)
      try_table (type $i32_exnref) (result i32 exnref) (catch_ref $e-depth $h)
        local.get $n  i32.const 1  i32.sub  call $recurse  unreachable
      end
    end
    throw_ref
  )

  (func $wrap (param i32) (result i32)
    block $top (result exnref)
      try_table (result exnref) (catch_all_ref $top)
        local.get 0  call $recurse  unreachable
      end
    end
    drop  i32.const 0
  )

  (func $main
    i32.const 3  call $wrap  drop  call $main
  )
)
  1. Convert the attached test case to test.wasm using wasm-tools.
  2. CLI command to run: wasmer run --llvm test.wasm --invoke main

Actual behavior

==1753205==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7bfeb7699b80 at pc 0x55555953bcee bp 0x7bfeb7699b50 sp 0x7bfeb7699328
WRITE of size 24 at 0x7bfeb7699b80 thread T0
    #0 0x55555953bced in sigaltstack (wasmer/target/x86_64-unknown-linux-gnu/release/wasmer+0x3fe7ced) (BuildId: 3a60d0e7d76d2907133901be07e0feb278ed431e)
    #1 0x55555955c7e9 in __asan::PlatformUnpoisonStacks() asan_posix.cpp
    #2 0x5555595609fc in __asan_handle_no_return (wasmer/target/x86_64-unknown-linux-gnu/release/wasmer+0x400c9fc) (BuildId: 3a60d0e7d76d2907133901be07e0feb278ed431e)
    #3 0x55555953def2 in _Unwind_RaiseException (wasmer/target/x86_64-unknown-linux-gnu/release/wasmer+0x3fe9ef2) (BuildId: 3a60d0e7d76d2907133901be07e0feb278ed431e)
    #4 0x55555fc8bf0b in wasmer_vm::libcalls::eh::gcc::throw wasmer/lib/vm/src/libcalls/eh/gcc.rs:294:13

Additional context

Although some other bug reports are based on distinct crash traces, we noticed an interesting relation/contrast to some other adjacent reports depending on the depth parameter of the wrap function defined above (i.e. the i32.const N instruction inside the main function). I will briefly list our observations below:

  1. Depth 1: Turns into → stack-buffer-underflow in wasmer_vm::store::InternalStoreHandle::get / VMExceptionObj::new_zeroed / wasmer_vm_alloc_exception (Stack-buffer-underflow in VMTag::get from recursive try_table/throw #6774)
  2. Depth 2: Turns into → plain stack-buffer-overflow in sigaltstack via wasmer_vm_throw directly, no unwinder frames (wasmer_vm_throw missing stack headroom check causes stack-buffer-overflow on deep recursion #6793)
  3. Depth 3: the eh::gcc::throw trace in this current issue through the unwinder path.

These might be within a similar class of stack-related memory bugs that are triggered along distinct paths. Depending on the path differences and further triaging, they may require separate fixes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions