mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-23 15:23:44 +02:00

According to AddressSanitizer, when `sandbox_malloc` causes the WebAssembly memory to grow in size, every single coroutine on the sandbox stack gets corrupted. So if `sandbox_malloc` is going to cause the memory to grow in size, we need to yield so that there are no coroutines on the sandbox stack while the reallocation occurs.
140 lines
5.3 KiB
C++
140 lines
5.3 KiB
C++
/*
|
|
** wasm-rt.cpp
|
|
**
|
|
** This file is part of mkxp.
|
|
**
|
|
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
|
|
**
|
|
** mkxp is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** mkxp is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include "core.h"
|
|
#include "wasm-rt.h"
|
|
|
|
#define WASM_PAGE_SIZE ((uint64_t)65536U)
|
|
|
|
#define WASM_MIN_PAGES ((uint32_t)1024U) // tentative
|
|
|
|
extern "C" bool wasm_rt_is_initialized(void) {
|
|
return true;
|
|
}
|
|
|
|
extern "C" WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t error) {
|
|
switch (error) {
|
|
case WASM_RT_TRAP_OOB:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 1: OOB (out-of-bounds memory access)\n");
|
|
break;
|
|
case WASM_RT_TRAP_INT_OVERFLOW:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 2: INT_OVERFLOW (arithmetic overflow)\n");
|
|
break;
|
|
case WASM_RT_TRAP_DIV_BY_ZERO:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 3: DIV_BY_ZERO (division by zero)\n");
|
|
break;
|
|
case WASM_RT_TRAP_INVALID_CONVERSION:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 4: INVALID_CONVERSION (invalid integer cast)\n");
|
|
break;
|
|
case WASM_RT_TRAP_UNREACHABLE:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 5: UNREACHABLE (hit an `unreachable' instruction in WebAssembly)\n");
|
|
break;
|
|
case WASM_RT_TRAP_CALL_INDIRECT:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 6: CALL_INDIRECT (attempted to call a function pointer with the wrong call signature)\n");
|
|
break;
|
|
case WASM_RT_TRAP_UNCAUGHT_EXCEPTION:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 7: UNCAUGHT_EXCEPTION (uncaught WebAssembly exception)\n");
|
|
break;
|
|
case WASM_RT_TRAP_EXHAUSTION:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error 8: EXHAUSTION (out of stack space)\n");
|
|
break;
|
|
default:
|
|
mkxp_retro::log_printf(RETRO_LOG_ERROR, "Sandbox error %d (unknown error)\n", error);
|
|
break;
|
|
}
|
|
std::abort();
|
|
}
|
|
|
|
extern "C" void wasm_rt_allocate_memory(wasm_rt_memory_t *memory, uint32_t initial_pages, uint32_t max_pages, bool is64) {
|
|
if ((memory->size = (uint64_t)initial_pages * WASM_PAGE_SIZE) > SIZE_MAX) {
|
|
throw std::bad_alloc();
|
|
}
|
|
memory->private_data = (uint8_t *)std::malloc(std::max((size_t)memory->size, (size_t)WASM_MIN_PAGES * (size_t)WASM_PAGE_SIZE));
|
|
if (memory->private_data == NULL) {
|
|
throw std::bad_alloc();
|
|
}
|
|
#ifdef MKXPZ_BIG_ENDIAN
|
|
memory->data = memory->private_data + std::max((size_t)memory->size, (size_t)WASM_MIN_PAGES * (size_t)WASM_PAGE_SIZE) - (size_t)memory->size;
|
|
#else
|
|
memory->data = memory->private_data;
|
|
#endif
|
|
memory->pages = initial_pages;
|
|
}
|
|
|
|
extern "C" uint32_t wasm_rt_grow_memory(wasm_rt_memory_t *memory, uint32_t pages) {
|
|
uint32_t new_pages = memory->pages + pages;
|
|
if (new_pages < memory->pages) { // Unsigned integer overflow
|
|
return -1;
|
|
}
|
|
|
|
uint64_t new_size = (uint64_t)new_pages * WASM_PAGE_SIZE;
|
|
if (new_size > SIZE_MAX) {
|
|
return -1;
|
|
}
|
|
|
|
if (!mkxp_retro::sandbox.has_value()) {
|
|
assert(new_pages <= WASM_MIN_PAGES);
|
|
} else if (mkxp_retro::sandbox->_rewinding()) {
|
|
if (!mkxp_retro::sandbox->_end_realloc()) {
|
|
return -1;
|
|
}
|
|
} else if (new_pages > WASM_MIN_PAGES) {
|
|
mkxp_retro::sandbox->_begin_realloc((size_t)new_size);
|
|
return -1; // Arbitrary value that never gets read
|
|
}
|
|
|
|
#ifdef MKXPZ_BIG_ENDIAN
|
|
memory->data = memory->private_data + std::max((size_t)new_size, (size_t)WASM_MIN_PAGES * (size_t)WASM_PAGE_SIZE) - (size_t)new_size;
|
|
#else
|
|
memory->data = memory->private_data;
|
|
#endif // MKXPZ_BIG_ENDIAN
|
|
|
|
uint32_t old_pages = memory->pages;
|
|
memory->pages = new_pages;
|
|
memory->size = new_size;
|
|
return old_pages;
|
|
}
|
|
|
|
extern "C" void wasm_rt_free_memory(wasm_rt_memory_t *memory) {
|
|
std::free(memory->private_data);
|
|
}
|
|
|
|
extern "C" void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t *table, uint32_t elements, uint32_t max_elements) {
|
|
table->private_data = new std::vector<wasm_rt_funcref_t>(elements);
|
|
table->data = ((std::vector<wasm_rt_funcref_t> *)table->private_data)->data();
|
|
table->size = elements;
|
|
}
|
|
|
|
extern "C" void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t *table) {
|
|
delete (std::vector<wasm_rt_funcref_t> *)table->private_data;
|
|
}
|
|
|
|
extern "C" uint32_t wasm_rt_push_funcref(wasm_rt_funcref_table_t *table, wasm_rt_funcref_t funcref) {
|
|
if (table->size == (uint32_t)-1) {
|
|
throw std::bad_alloc();
|
|
}
|
|
((std::vector<wasm_rt_funcref_t> *)table->private_data)->push_back(funcref);
|
|
table->data = ((std::vector<wasm_rt_funcref_t> *)table->private_data)->data();
|
|
return table->size++;
|
|
}
|