Encrylize edited issue #3061:
Test Case
trap.c
:#include <wasm.h> #include <wasmtime.h> int main(void) { wasm_engine_t* engine = wasm_engine_new(); wasm_store_t* store = wasm_store_new(engine); wasm_byte_vec_t binary; const char* wat = "(module (func (export \"trap\") (unreachable)))"; wasmtime_wat2wasm(wat, strlen(wat), &binary); wasm_module_t* module = wasm_module_new(store, &binary); wasm_byte_vec_delete(&binary); wasm_extern_vec_t imports = WASM_EMPTY_VEC; wasm_instance_t* instance = wasm_instance_new(store, module, &imports, NULL); wasm_extern_vec_t exports; wasm_instance_exports(instance, &exports); const wasm_func_t* func = wasm_extern_as_func(exports.data[0]); const wasm_val_vec_t args = WASM_EMPTY_VEC; wasm_val_vec_t results = WASM_EMPTY_VEC; wasm_trap_t* trap = wasm_func_call(func, &args, &results); wasm_trap_delete(trap); wasm_extern_vec_delete(&exports); wasm_module_delete(module); wasm_instance_delete(instance); wasm_store_delete(store); wasm_engine_delete(engine); return 0; }
Steps to Reproduce
- Compile program:
gcc trap.c -lwasmtime -o trap
- Execute program using LLDB:
lldb ./trap
- Execute program using Valgrind:
valgrind ./trap
Expected Results
The
UD2
instruction should not be executed and no warnings should be emitted by Valgrind.Actual Results
The
UD2
instruction is executed as shown by LLDB:$ lldb ./trap (lldb) target create "./trap" Current executable set to '/home/jfb/projects/wasm/trap' (x86_64). (lldb) r Process 301185 launched: '/home/jfb/projects/wasm/trap' (x86_64) Process 301185 stopped * thread #1, name = 'trap', stop reason = signal SIGILL: illegal instruction operand frame #0: 0x00007ffff7fb7004 -> 0x7ffff7fb7004: ud2 0x7ffff7fb7006: pushq %rbp 0x7ffff7fb7007: movq %rsp, %rbp 0x7ffff7fb700a: callq *%rdx (lldb) c Process 301185 resuming Process 301185 exited with status = 0 (0x00000000) (lldb) exit
Valgrind emits a warning about the client switching stacks:
$ valgrind ./trap ==302700== Memcheck, a memory error detector ==302700== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==302700== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info ==302700== Command: ./trap ==302700== ==302700== valgrind: Unrecognised instruction at address 0x4850004. ==302700== at 0x4850004: ??? ==302700== by 0x485000B: ??? ==302700== by 0x4E2548F: wasmtime_setjmp (in /usr/lib/libwasmtime.so) ==302700== by 0x4980838: wasmtime_runtime::traphandlers::catch_traps (in /usr/lib/libwasmtime.so) ==302700== by 0x496DE16: wasmtime::func::Func::call (in /usr/lib/libwasmtime.so) ==302700== by 0x4A22DE3: wasm_func_call (in /usr/lib/libwasmtime.so) ==302700== by 0x10935D: main (in /home/jfb/projects/wasm/trap) ==302700== Your program just tried to execute an instruction that Valgrind ==302700== did not recognise. There are two possible reasons for this. ==302700== 1. Your program has a bug and erroneously jumped to a non-code ==302700== location. If you are running Memcheck and you just saw a ==302700== warning about a bad jump, it's probably your program's fault. ==302700== 2. The instruction is legitimate but Valgrind doesn't handle it, ==302700== i.e. it's Valgrind's fault. If you think this is the case or ==302700== you are not sure, please let us know and we'll try to fix it. ==302700== Either way, Valgrind will now raise a SIGILL signal which will ==302700== probably kill your program. ==302700== Warning: client switching stacks? SP change: 0x5aaa068 --> 0x1ffefffd00 ==302700== to suppress, use: --max-stackframe=137327107224 or greater ==302700== ==302700== HEAP SUMMARY: ==302700== in use at exit: 920 bytes in 7 blocks ==302700== total heap usage: 659 allocs, 652 frees, 152,738 bytes allocated ==302700== ==302700== LEAK SUMMARY: ==302700== definitely lost: 0 bytes in 0 blocks ==302700== indirectly lost: 0 bytes in 0 blocks ==302700== possibly lost: 0 bytes in 0 blocks ==302700== still reachable: 920 bytes in 7 blocks ==302700== suppressed: 0 bytes in 0 blocks ==302700== Rerun with --leak-check=full to see details of leaked memory ==302700== ==302700== For lists of detected and suppressed errors, rerun with: -s ==302700== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Versions and Environment
Wasmtime version or commit: Commit
c71ad9490
Operating system: Arch Linux
Architecture: x86_64
cfallin commented on issue #3061:
Hi @Encrylize,
The undefined instruction (
ud2
on x86-64) is how wasmtime transfers control to its trap handlers; executing this instruction is the intended behavior when a trap occurs.Normally, wasmtime will install a signal handler for
SIGILL
(or the equivalent mechanism with Mach ports on macOS or Win32 APIs on Windows), catch the signal, and longjmp back to the top-level entry point, at which point it can return the trap error-code to the caller.However, if you are running under a debugger or under Valgrind, it's possible that this tool will catch the signal before wasmtime does. There is nothing that we can really do about this, as far as I know: the tools have complete control over inspecting the process (gdb/lldb via the
ptrace()
syscall, Valgrind because it actually emulates execution with a JIT layer), and have full visibility of signals.I know that at least Valgrind has the concept of "suppressions", and gdb allows setting the default action for a signal (such as
SIGILL
) to "ignore"; perhaps one of these mechanisms would help in your case?In any case, using signals to catch traps is standard practice for JIT engines and other runtimes, and is a fully correct and supported use of the signal-handler mechanism, so this is not a bug that we can resolve, I think. Thank you for filing the report regardless!
cfallin closed issue #3061:
Test Case
trap.c
:#include <wasm.h> #include <wasmtime.h> int main(void) { wasm_engine_t* engine = wasm_engine_new(); wasm_store_t* store = wasm_store_new(engine); wasm_byte_vec_t binary; const char* wat = "(module (func (export \"trap\") (unreachable)))"; wasmtime_wat2wasm(wat, strlen(wat), &binary); wasm_module_t* module = wasm_module_new(store, &binary); wasm_byte_vec_delete(&binary); wasm_extern_vec_t imports = WASM_EMPTY_VEC; wasm_instance_t* instance = wasm_instance_new(store, module, &imports, NULL); wasm_extern_vec_t exports; wasm_instance_exports(instance, &exports); const wasm_func_t* func = wasm_extern_as_func(exports.data[0]); const wasm_val_vec_t args = WASM_EMPTY_VEC; wasm_val_vec_t results = WASM_EMPTY_VEC; wasm_trap_t* trap = wasm_func_call(func, &args, &results); wasm_trap_delete(trap); wasm_extern_vec_delete(&exports); wasm_module_delete(module); wasm_instance_delete(instance); wasm_store_delete(store); wasm_engine_delete(engine); return 0; }
Steps to Reproduce
- Compile program:
gcc trap.c -lwasmtime -o trap
- Execute program using LLDB:
lldb ./trap
- Execute program using Valgrind:
valgrind ./trap
Expected Results
The
UD2
instruction should not be executed and no warnings should be emitted by Valgrind.Actual Results
The
UD2
instruction is executed as shown by LLDB:$ lldb ./trap (lldb) target create "./trap" Current executable set to '/home/jfb/projects/wasm/trap' (x86_64). (lldb) r Process 301185 launched: '/home/jfb/projects/wasm/trap' (x86_64) Process 301185 stopped * thread #1, name = 'trap', stop reason = signal SIGILL: illegal instruction operand frame #0: 0x00007ffff7fb7004 -> 0x7ffff7fb7004: ud2 0x7ffff7fb7006: pushq %rbp 0x7ffff7fb7007: movq %rsp, %rbp 0x7ffff7fb700a: callq *%rdx (lldb) c Process 301185 resuming Process 301185 exited with status = 0 (0x00000000) (lldb) exit
Valgrind emits a warning about the client switching stacks:
$ valgrind ./trap ==302700== Memcheck, a memory error detector ==302700== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==302700== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info ==302700== Command: ./trap ==302700== ==302700== valgrind: Unrecognised instruction at address 0x4850004. ==302700== at 0x4850004: ??? ==302700== by 0x485000B: ??? ==302700== by 0x4E2548F: wasmtime_setjmp (in /usr/lib/libwasmtime.so) ==302700== by 0x4980838: wasmtime_runtime::traphandlers::catch_traps (in /usr/lib/libwasmtime.so) ==302700== by 0x496DE16: wasmtime::func::Func::call (in /usr/lib/libwasmtime.so) ==302700== by 0x4A22DE3: wasm_func_call (in /usr/lib/libwasmtime.so) ==302700== by 0x10935D: main (in /home/jfb/projects/wasm/trap) ==302700== Your program just tried to execute an instruction that Valgrind ==302700== did not recognise. There are two possible reasons for this. ==302700== 1. Your program has a bug and erroneously jumped to a non-code ==302700== location. If you are running Memcheck and you just saw a ==302700== warning about a bad jump, it's probably your program's fault. ==302700== 2. The instruction is legitimate but Valgrind doesn't handle it, ==302700== i.e. it's Valgrind's fault. If you think this is the case or ==302700== you are not sure, please let us know and we'll try to fix it. ==302700== Either way, Valgrind will now raise a SIGILL signal which will ==302700== probably kill your program. ==302700== Warning: client switching stacks? SP change: 0x5aaa068 --> 0x1ffefffd00 ==302700== to suppress, use: --max-stackframe=137327107224 or greater ==302700== ==302700== HEAP SUMMARY: ==302700== in use at exit: 920 bytes in 7 blocks ==302700== total heap usage: 659 allocs, 652 frees, 152,738 bytes allocated ==302700== ==302700== LEAK SUMMARY: ==302700== definitely lost: 0 bytes in 0 blocks ==302700== indirectly lost: 0 bytes in 0 blocks ==302700== possibly lost: 0 bytes in 0 blocks ==302700== still reachable: 920 bytes in 7 blocks ==302700== suppressed: 0 bytes in 0 blocks ==302700== Rerun with --leak-check=full to see details of leaked memory ==302700== ==302700== For lists of detected and suppressed errors, rerun with: -s ==302700== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Versions and Environment
Wasmtime version or commit: Commit
c71ad9490
Operating system: Arch Linux
Architecture: x86_64
Last updated: Dec 23 2024 at 13:07 UTC