Stream: git-wasmtime

Topic: wasmtime / issue #3061 UD2 is executed and Valgrind emits...


view this post on Zulip Wasmtime GitHub notifications bot (Jul 04 2021 at 16:27):

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

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

view this post on Zulip Wasmtime GitHub notifications bot (Jul 04 2021 at 21:19):

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!

view this post on Zulip Wasmtime GitHub notifications bot (Jul 04 2021 at 21:20):

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

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