Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Wasm Exceptions] Crash in scan_eh_tab() #13548

Closed
kripken opened this issue Feb 23, 2021 · 0 comments
Closed

[Wasm Exceptions] Crash in scan_eh_tab() #13548

kripken opened this issue Feb 23, 2021 · 0 comments

Comments

@kripken
Copy link
Member

kripken commented Feb 23, 2021

STR:

#include <stdio.h> // avoid iostream C++ code, just test libc++abi, not libc++
#include <stdint.h>

const int INIIAL_FUEL = 100;

static int fuel = INIIAL_FUEL;

void refuel() {
  fuel = INIIAL_FUEL;
}

// TODO random data
static bool boolean = true;

bool getBoolean() {
  // If we are done, exit all loops etc.
  if (fuel == 0) {
    return false;
  }
  fuel--;
  boolean = !boolean;
  return boolean;
}

struct Class {
  Class();
  ~Class();
};

Class::Class() {
  puts("class-instance");
}

Class::~Class() {
  puts("~class-instance");
}

void func_0() {
  try {
    throw 0;
  } catch (Class) {
  }
}

void func_1() {
  try {
    throw 0;
  } catch (int32_t) {
    try {
      func_0();
    } catch (int32_t) {
    }
  }
}

int main() {
  // func_0
  puts("calling func_0");
  refuel();
  try {
    func_0();
  } catch (...) {
    puts("main caught from func_0");
  }
  // func_1
  puts("calling func_1");
  refuel();
  try {
    func_1();
  } catch (...) {
    puts("main caught from func_1");
  }
  return 0;
}
em++ a.cpp -fwasm-exceptions -s WASM_BIGINT -g
v8 --experimental-wasm-eh a.out.js

-g is useful for the stack trace, but not necessary to get a runtime error. The trace is

calling func_0
main caught from func_0
calling func_1
exception thrown: RuntimeError: unreachable,RuntimeError: unreachable
    at abort_message (<anonymous>:wasm-function[14]:0x7fc)
    at demangling_terminate_handler() (<anonymous>:wasm-function[15]:0x8c5)
    at std::__terminate(void (*)()) (<anonymous>:wasm-function[22]:0xa10)
    at __cxxabiv1::call_terminate(bool, _Unwind_Exception*) (<anonymous>:wasm-function[80]:0x1efb)
    at __cxxabiv1::scan_eh_tab(__cxxabiv1::(anonymous namespace)::scan_results&, _Unwind_Action, bool, _Unwind_Exception*, _Unwind_Context*) (<anonymous>:wasm-function[78]:0x1eae)
    at __gxx_personality_wasm0 (<anonymous>:wasm-function[77]:0x19f4)
    at _Unwind_CallPersonality (<anonymous>:wasm-function[94]:0x2330)
    at func_1() (<anonymous>:wasm-function[9]:0x5b9)
    at __original_main (<anonymous>:wasm-function[10]:0x733)
    at main (<anonymous>:wasm-function[11]:0x7dc)

Tested on the #13485 branch where this was found, which skips building libc++ - perhaps that is related?

morehouse pushed a commit to morehouse/llvm-project that referenced this issue Mar 4, 2021
In every catchpad except `catch (...)`, we add a call to
`_Unwind_CallPersonality`, which is a wapper to call the personality
function. (In most of other Itanium-based architectures the call is done
from libunwind, but in wasm we don't have the control over the VM.)
Because the personatlity function is called to figure out whether the
current exception is a type we should catch, such as `int` or
`SomeClass&`, `catch (...)` does not need the personality function call.
For the same reason, all cleanuppads don't need it.

When we call `_Unwind_CallPersonality`, we store some necessary info in
a data structure called `__wasm_lpad_context` of type
`_Unwind_LandingPadContext`, which is defined  in the wasm's port of
libunwind in Emscripten. Also the personality wrapper function returns
some info (selector and the caught pointer) in that data structure, so
it is used as a medium for communication.

One of the info we need to store is the address for LSDA info for the
current function. `wasm.lsda()` intrinsic returns that address. (This
intrinsic will be lowered to a symbol that points to the LSDA address.)
The simpliest thing is call `wasm.lsda()` every time we need to call
`_Unwind_CallPersonality` and store that info in `__wasm_lpad_context`
data structure. But we tried to be better than that (D77423 and some
more previous CLs), so if catchpad A dominates catchpad B and catchpad A
is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B,
thinking that the LSDA address is the same for a single function and we
already visited catchpad A and `__wasm_lpad_context.lsda` field would
already have that value.

But this can be incorrect if there is a call to another function, which
also can have the personality function and LSDA, between catchpad A and
catchpad B, because `__wasm_lpad_context` is a globally defined
structure and the callee function will overwrite its `lsda` field.

So in this CL we don't try to do any optimizaions on adding
`wasm.lsda()` call; we store the result of `wasm.lsda()` every time we
call `_Unwind_CallPersonality`. We can do some complicated analysis,
like checking if there is a function call between the dominating
catchpad and the current catchpad, but at this time it seems overkill.

This deletes three tests because they all tested `wasm.ldsa()` call
optimization.

Fixes emscripten-core/emscripten#13548.

Reviewed By: tlively

Differential Revision: https://reviews.llvm.org/D97309
mem-frob pushed a commit to draperlaboratory/hope-llvm-project that referenced this issue Oct 7, 2022
In every catchpad except `catch (...)`, we add a call to
`_Unwind_CallPersonality`, which is a wapper to call the personality
function. (In most of other Itanium-based architectures the call is done
from libunwind, but in wasm we don't have the control over the VM.)
Because the personatlity function is called to figure out whether the
current exception is a type we should catch, such as `int` or
`SomeClass&`, `catch (...)` does not need the personality function call.
For the same reason, all cleanuppads don't need it.

When we call `_Unwind_CallPersonality`, we store some necessary info in
a data structure called `__wasm_lpad_context` of type
`_Unwind_LandingPadContext`, which is defined  in the wasm's port of
libunwind in Emscripten. Also the personality wrapper function returns
some info (selector and the caught pointer) in that data structure, so
it is used as a medium for communication.

One of the info we need to store is the address for LSDA info for the
current function. `wasm.lsda()` intrinsic returns that address. (This
intrinsic will be lowered to a symbol that points to the LSDA address.)
The simpliest thing is call `wasm.lsda()` every time we need to call
`_Unwind_CallPersonality` and store that info in `__wasm_lpad_context`
data structure. But we tried to be better than that (D77423 and some
more previous CLs), so if catchpad A dominates catchpad B and catchpad A
is not `catch (...)`, we didn't insert `wasm.lsda()` call in catchpad B,
thinking that the LSDA address is the same for a single function and we
already visited catchpad A and `__wasm_lpad_context.lsda` field would
already have that value.

But this can be incorrect if there is a call to another function, which
also can have the personality function and LSDA, between catchpad A and
catchpad B, because `__wasm_lpad_context` is a globally defined
structure and the callee function will overwrite its `lsda` field.

So in this CL we don't try to do any optimizaions on adding
`wasm.lsda()` call; we store the result of `wasm.lsda()` every time we
call `_Unwind_CallPersonality`. We can do some complicated analysis,
like checking if there is a function call between the dominating
catchpad and the current catchpad, but at this time it seems overkill.

This deletes three tests because they all tested `wasm.ldsa()` call
optimization.

Fixes emscripten-core/emscripten#13548.

Reviewed By: tlively

Differential Revision: https://reviews.llvm.org/D97309
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant