/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm-rt-impl.h"
#include <assert.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if WASM_RT_INSTALL_SIGNAL_HANDLER && !
defined(_WIN32)
#include <signal.h>
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#ifndef NDEBUG
#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__);
#else
#define DEBUG_PRINTF(...)
#endif
#if WASM_RT_INSTALL_SIGNAL_HANDLER
static bool g_signal_handler_installed =
false;
#ifdef _WIN32
static void* g_sig_handler_handle = 0;
#endif
#endif
#if WASM_RT_USE_SEGUE
// Currently Segue is used only for linux
#include <sys/auxv.h>
#ifdef __GLIBC__
#include <gnu/libc-version.h>
#endif
bool wasm_rt_fsgsbase_inst_supported =
false;
#include <
asm/prctl.h>
// For ARCH_SET_GS
#include <sys/syscall.h>
// For SYS_arch_prctl
#include <unistd.h>
// For syscall
#ifndef HWCAP2_FSGSBASE
#define HWCAP2_FSGSBASE (1 << 1)
#endif
#endif
#if WASM_RT_SEGUE_FREE_SEGMENT
WASM_RT_THREAD_LOCAL
void* wasm_rt_last_segment_val = NULL;
#endif
#if WASM_RT_STACK_DEPTH_COUNT
WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth;
WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth;
#elif WASM_RT_STACK_EXHAUSTION_HANDLER
static WASM_RT_THREAD_LOCAL
void* g_alt_stack = NULL;
#endif
#ifndef WASM_RT_TRAP_HANDLER
WASM_RT_THREAD_LOCAL wasm_rt_jmp_buf g_wasm_rt_jmp_buf;
#endif
#ifdef WASM_RT_TRAP_HANDLER
extern void WASM_RT_TRAP_HANDLER(wasm_rt_trap_t code);
#endif
void wasm_rt_trap(wasm_rt_trap_t code) {
assert(code != WASM_RT_TRAP_NONE);
#if WASM_RT_STACK_DEPTH_COUNT
wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth;
#endif
#ifdef WASM_RT_TRAP_HANDLER
WASM_RT_TRAP_HANDLER(code);
wasm_rt_unreachable();
#else
WASM_RT_LONGJMP(g_wasm_rt_jmp_buf, code);
#endif
}
#ifdef _WIN32
#if WASM_RT_INSTALL_SIGNAL_HANDLER
static LONG os_signal_handler(PEXCEPTION_POINTERS info) {
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
wasm_rt_trap(WASM_RT_TRAP_OOB);
}
else if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
}
return EXCEPTION_CONTINUE_SEARCH;
}
static void os_install_signal_handler(
void) {
g_sig_handler_handle =
AddVectoredExceptionHandler(1
/* CALL_FIRST */, os_signal_handler);
}
static void os_cleanup_signal_handler(
void) {
RemoveVectoredExceptionHandler(g_sig_handler_handle);
}
#endif
#else
#if WASM_RT_INSTALL_SIGNAL_HANDLER
static void os_signal_handler(
int sig, siginfo_t* si,
void* unused) {
if (si->si_code == SEGV_ACCERR) {
wasm_rt_trap(WASM_RT_TRAP_OOB);
}
else {
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
}
}
static void os_install_signal_handler(
void) {
struct sigaction sa;
memset(&sa,
'\0',
sizeof(sa));
sa.sa_flags = SA_SIGINFO;
#if WASM_RT_STACK_EXHAUSTION_HANDLER
sa.sa_flags |= SA_ONSTACK;
#endif
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = os_signal_handler;
/* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) {
perror(
"sigaction failed");
abort();
}
}
static void os_cleanup_signal_handler(
void) {
/* Undo what was done in os_install_signal_handler */
struct sigaction sa;
memset(&sa,
'\0',
sizeof(sa));
sa.sa_handler = SIG_DFL;
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) {
perror(
"sigaction failed");
abort();
}
}
#endif
#if WASM_RT_STACK_EXHAUSTION_HANDLER
static bool os_has_altstack_installed() {
/* check for altstack already in place */
stack_t ss;
if (sigaltstack(NULL, &ss) != 0) {
perror(
"sigaltstack failed");
abort();
}
return !(ss.ss_flags & SS_DISABLE);
}
/* These routines set up an altstack to handle SIGSEGV from stack overflow. */
static void os_allocate_and_install_altstack(
void) {
/* verify altstack not already allocated */
assert(!g_alt_stack &&
"wasm-rt error: tried to re-allocate thread-local alternate stack");
/* We could check and warn if an altstack is already installed, but some
* sanitizers install their own altstack, so this warning would fire
* spuriously and break the test outputs. */
/* allocate altstack */
g_alt_stack = malloc(SIGSTKSZ);
if (g_alt_stack == NULL) {
perror(
"malloc failed");
abort();
}
/* install altstack */
stack_t ss;
ss.ss_sp = g_alt_stack;
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
if (sigaltstack(&ss, NULL) != 0) {
perror(
"sigaltstack failed");
abort();
}
}
static void os_disable_and_deallocate_altstack(
void) {
/* in debug build, verify altstack allocated */
assert(g_alt_stack &&
"wasm-rt error: thread-local alternate stack not allocated");
/* verify altstack was still in place */
stack_t ss;
if (sigaltstack(NULL, &ss) != 0) {
perror(
"sigaltstack failed");
abort();
}
if ((!g_alt_stack) || (ss.ss_flags & SS_DISABLE) ||
(ss.ss_sp != g_alt_stack) || (ss.ss_size != SIGSTKSZ)) {
DEBUG_PRINTF(
"wasm-rt warning: alternate stack was modified unexpectedly\n");
return;
}
/* disable and free */
ss.ss_flags = SS_DISABLE;
if (sigaltstack(&ss, NULL) != 0) {
perror(
"sigaltstack failed");
abort();
}
assert(!os_has_altstack_installed());
free(g_alt_stack);
g_alt_stack = NULL;
}
#endif
#endif
void wasm_rt_init(
void) {
wasm_rt_init_thread();
#if WASM_RT_INSTALL_SIGNAL_HANDLER
if (!g_signal_handler_installed) {
g_signal_handler_installed =
true;
os_install_signal_handler();
}
#endif
#if WASM_RT_USE_SEGUE
#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 18
// Check for support for userspace wrgsbase instructions
unsigned long val = getauxval(AT_HWCAP2);
wasm_rt_fsgsbase_inst_supported = val & HWCAP2_FSGSBASE;
#endif
#endif
assert(wasm_rt_is_initialized());
}
bool wasm_rt_is_initialized(
void) {
#if WASM_RT_STACK_EXHAUSTION_HANDLER
if (!os_has_altstack_installed()) {
return false;
}
#endif
#if WASM_RT_INSTALL_SIGNAL_HANDLER
return g_signal_handler_installed;
#else
return true;
#endif
}
void wasm_rt_free(
void) {
assert(wasm_rt_is_initialized());
#if WASM_RT_INSTALL_SIGNAL_HANDLER
os_cleanup_signal_handler();
g_signal_handler_installed =
false;
#endif
wasm_rt_free_thread();
}
void wasm_rt_init_thread(
void) {
#if WASM_RT_STACK_EXHAUSTION_HANDLER
os_allocate_and_install_altstack();
#endif
}
void wasm_rt_free_thread(
void) {
#if WASM_RT_STACK_EXHAUSTION_HANDLER
os_disable_and_deallocate_altstack();
#endif
}
#if WASM_RT_USE_SEGUE
void wasm_rt_syscall_set_segue_base(
void* base) {
if (syscall(SYS_arch_prctl, ARCH_SET_GS, base) != 0) {
perror(
"wasm_rt_syscall_set_segue_base error");
abort();
}
}
void* wasm_rt_syscall_get_segue_base() {
void* base;
if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) {
perror(
"wasm_rt_syscall_get_segue_base error");
abort();
}
return base;
}
#endif
// Include table operations for funcref
#define WASM_RT_TABLE_OPS_FUNCREF
#include "wasm-rt-impl-tableops.inc"
#undef WASM_RT_TABLE_OPS_FUNCREF
// Include table operations for externref
#define WASM_RT_TABLE_OPS_EXTERNREF
#include "wasm-rt-impl-tableops.inc"
#undef WASM_RT_TABLE_OPS_EXTERNREF
const char* wasm_rt_strerror(wasm_rt_trap_t trap) {
switch (trap) {
case WASM_RT_TRAP_NONE:
return "No error";
case WASM_RT_TRAP_OOB:
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
return "Out-of-bounds access in linear memory or a table, or call stack "
"exhausted";
#else
return "Out-of-bounds access in linear memory or a table";
case WASM_RT_TRAP_EXHAUSTION:
return "Call stack exhausted";
#endif
case WASM_RT_TRAP_INT_OVERFLOW:
return "Integer overflow on divide or truncation";
case WASM_RT_TRAP_DIV_BY_ZERO:
return "Integer divide by zero";
case WASM_RT_TRAP_INVALID_CONVERSION:
return "Conversion from NaN to integer";
case WASM_RT_TRAP_UNREACHABLE:
return "Unreachable instruction executed";
case WASM_RT_TRAP_CALL_INDIRECT:
return "Invalid call_indirect or return_call_indirect";
case WASM_RT_TRAP_UNCAUGHT_EXCEPTION:
return "Uncaught exception";
case WASM_RT_TRAP_UNALIGNED:
return "Unaligned atomic memory access";
}
return "invalid trap code";
}