diff options
Diffstat (limited to 'third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c')
-rw-r--r-- | third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c b/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c new file mode 100644 index 0000000000..1bdf6f715c --- /dev/null +++ b/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c @@ -0,0 +1,454 @@ +#include "wasm2c_rt_mem.h" +#include "wasm-rt.h" + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +enum +{ + MMAP_PROT_NONE = 0, + MMAP_PROT_READ = 1, + MMAP_PROT_WRITE = 2, + MMAP_PROT_EXEC = 4 +}; + +/* Memory map flags */ +enum +{ + MMAP_MAP_NONE = 0, + /* Put the mapping into 0 to 2 G, supported only on x86_64 */ + MMAP_MAP_32BIT = 1, + /* Don't interpret addr as a hint: place the mapping at exactly + that address. */ + MMAP_MAP_FIXED = 2 +}; + +// Try reserving an aligned memory space. +// Returns pointer to allocated space on success, 0 on failure. +static void* os_mmap_aligned(void* addr, + size_t requested_length, + int prot, + int flags, + size_t alignment, + size_t alignment_offset); +// Unreserve the memory space +static void os_munmap(void* addr, size_t size); +// Allocates and sets the permissions on the previously reserved memory space +// Returns 0 on success, non zero on failure. +static int os_mmap_commit(void* curr_heap_end_pointer, + size_t expanded_size, + int prot); + +wasm_rt_memory_t* w2c_env_memory(struct w2c_env* instance) +{ + return instance->sandbox_memory_info; +} + +wasm_rt_funcref_table_t* w2c_env_0x5F_indirect_function_table( + struct w2c_env* instance) +{ + return instance->sandbox_callback_table; +} + +#define WASM_PAGE_SIZE 65536 +#define RLBOX_FOUR_GIG 0x100000000ull + +#if UINTPTR_MAX == 0xffffffffffffffff +// Guard page of 4GiB +# define WASM_HEAP_GUARD_PAGE_SIZE 0x100000000ull +// Heap aligned to 4GB +# define WASM_HEAP_ALIGNMENT 0x100000000ull +// By default max heap is 4GB +# define WASM_HEAP_DEFAULT_MAX_PAGES 65536 +#elif UINTPTR_MAX == 0xffffffff +// No guard pages +# define WASM_HEAP_GUARD_PAGE_SIZE 0 +// Unaligned heap +# define WASM_HEAP_ALIGNMENT 0 +// Default max heap is 16MB +# define WASM_HEAP_DEFAULT_MAX_PAGES 256 +#else +# error "Unknown pointer size" +#endif + +static uint64_t compute_heap_reserve_space(uint32_t chosen_max_pages) +{ + const uint64_t heap_reserve_size = + ((uint64_t)chosen_max_pages) * WASM_PAGE_SIZE + WASM_HEAP_GUARD_PAGE_SIZE; + return heap_reserve_size; +} + +w2c_mem_capacity get_valid_wasm2c_memory_capacity(uint64_t min_capacity, + bool is_mem_32) +{ + const w2c_mem_capacity err_val = { false /* is_valid */, + false /* is_mem_32 */, + 0 /* max_pages */, + 0 /* max_size */ }; + + // We do not handle memory 64 + if (!is_mem_32) { + return err_val; + } + + const uint64_t default_capacity = + ((uint64_t)WASM_HEAP_DEFAULT_MAX_PAGES) * WASM_PAGE_SIZE; + + if (min_capacity <= default_capacity) { + // Handle 0 case and small values + const w2c_mem_capacity ret = { true /* is_valid */, + true /* is_mem_32 */, + WASM_HEAP_DEFAULT_MAX_PAGES /* max_pages */, + default_capacity /* max_size */ }; + return ret; + } else if (min_capacity > UINT32_MAX) { + // Handle out of range values + return err_val; + } + + const uint64_t page_size_minus_1 = WASM_PAGE_SIZE - 1; + // Get number of pages greater than min_capacity + const uint64_t capacity_pages = ((min_capacity - 1) / page_size_minus_1) + 1; + + const w2c_mem_capacity ret = { true /* is_valid */, + true /* is_mem_32 */, + capacity_pages /* max_pages */, + capacity_pages * + WASM_PAGE_SIZE /* max_size */ }; + return ret; +} + +wasm_rt_memory_t create_wasm2c_memory(uint32_t initial_pages, + const w2c_mem_capacity* custom_capacity) +{ + + if (custom_capacity && !custom_capacity->is_valid) { + wasm_rt_memory_t ret = { 0 }; + return ret; + } + + const uint32_t byte_length = initial_pages * WASM_PAGE_SIZE; + const uint64_t chosen_max_pages = + custom_capacity ? custom_capacity->max_pages : WASM_HEAP_DEFAULT_MAX_PAGES; + const uint64_t heap_reserve_size = + compute_heap_reserve_space(chosen_max_pages); + + uint8_t* data = 0; + const uint64_t retries = 10; + for (uint64_t i = 0; i < retries; i++) { + data = (uint8_t*)os_mmap_aligned(0, + heap_reserve_size, + MMAP_PROT_NONE, + MMAP_MAP_NONE, + WASM_HEAP_ALIGNMENT, + 0 /* alignment_offset */); + if (data) { + int ret = + os_mmap_commit(data, byte_length, MMAP_PROT_READ | MMAP_PROT_WRITE); + if (ret != 0) { + // failed to set permissions + os_munmap(data, heap_reserve_size); + data = 0; + } + break; + } + } + + wasm_rt_memory_t ret; + ret.data = data; + ret.max_pages = chosen_max_pages; + ret.pages = initial_pages; + ret.size = byte_length; + return ret; +} + +void destroy_wasm2c_memory(wasm_rt_memory_t* memory) +{ + if (memory->data != 0) { + const uint64_t heap_reserve_size = + compute_heap_reserve_space(memory->max_pages); + os_munmap(memory->data, heap_reserve_size); + memory->data = 0; + } +} + +#undef WASM_HEAP_DEFAULT_MAX_PAGES +#undef WASM_HEAP_ALIGNMENT +#undef WASM_HEAP_GUARD_PAGE_SIZE +#undef RLBOX_FOUR_GIG +#undef WASM_PAGE_SIZE + +// Based on +// https://web.archive.org/web/20191012035921/http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system#BSD +// Check for windows (non cygwin) environment +#if defined(_WIN32) + +# include <windows.h> + +static size_t os_getpagesize() +{ + SYSTEM_INFO S; + GetNativeSystemInfo(&S); + return S.dwPageSize; +} + +static void* win_mmap(void* hint, + size_t size, + int prot, + int flags, + DWORD alloc_flag) +{ + DWORD flProtect = PAGE_NOACCESS; + size_t request_size, page_size; + void* addr; + + page_size = os_getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + + if (request_size < size) + /* integer overflow */ + return NULL; + + if (request_size == 0) + request_size = page_size; + + if (prot & MMAP_PROT_EXEC) { + if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_EXECUTE_READ; + } else if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_READWRITE; + else if (prot & MMAP_PROT_READ) + flProtect = PAGE_READONLY; + + addr = VirtualAlloc((LPVOID)hint, request_size, alloc_flag, flProtect); + return addr; +} + +static void* os_mmap_aligned(void* addr, + size_t requested_length, + int prot, + int flags, + size_t alignment, + size_t alignment_offset) +{ + size_t padded_length = requested_length + alignment + alignment_offset; + uintptr_t unaligned = + (uintptr_t)win_mmap(addr, padded_length, prot, flags, MEM_RESERVE); + + if (!unaligned) { + return (void*)unaligned; + } + + // Round up the next address that has addr % alignment = 0 + const size_t alignment_corrected = alignment == 0 ? 1 : alignment; + uintptr_t aligned_nonoffset = + (unaligned + (alignment_corrected - 1)) & ~(alignment_corrected - 1); + + // Currently offset 0 is aligned according to alignment + // Alignment needs to be enforced at the given offset + uintptr_t aligned = 0; + if ((aligned_nonoffset - alignment_offset) >= unaligned) { + aligned = aligned_nonoffset - alignment_offset; + } else { + aligned = aligned_nonoffset - alignment_offset + alignment; + } + + if (aligned == unaligned && padded_length == requested_length) { + return (void*)aligned; + } + + // Sanity check + if (aligned < unaligned || + (aligned + (requested_length - 1)) > (unaligned + (padded_length - 1)) || + (aligned + alignment_offset) % alignment_corrected != 0) { + os_munmap((void*)unaligned, padded_length); + return NULL; + } + + // windows does not support partial unmapping, so unmap and remap + os_munmap((void*)unaligned, padded_length); + aligned = (uintptr_t)win_mmap( + (void*)aligned, requested_length, prot, flags, MEM_RESERVE); + return (void*)aligned; +} + +static void os_munmap(void* addr, size_t size) +{ + DWORD alloc_flag = MEM_RELEASE; + if (addr) { + if (VirtualFree(addr, 0, alloc_flag) == 0) { + size_t page_size = os_getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + int64_t curr_err = errno; + printf("os_munmap error addr:%p, size:0x%zx, errno:%" PRId64 "\n", + addr, + request_size, + curr_err); + } + } +} + +static int os_mmap_commit(void* curr_heap_end_pointer, + size_t expanded_size, + int prot) +{ + uintptr_t addr = (uintptr_t)win_mmap( + curr_heap_end_pointer, expanded_size, prot, MMAP_MAP_NONE, MEM_COMMIT); + int ret = addr ? 0 : -1; + return ret; +} + +#elif !defined(_WIN32) && (defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__))) + +# include <sys/mman.h> +# include <unistd.h> + +static size_t os_getpagesize() +{ + return getpagesize(); +} + +static void* os_mmap(void* hint, size_t size, int prot, int flags) +{ + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint64_t request_size, page_size; + void* addr; + + page_size = (uint64_t)os_getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + + if ((size_t)request_size < size) + /* integer overflow */ + return NULL; + + if (request_size > 16 * (uint64_t)UINT32_MAX) + /* At most 16 G is allowed */ + return NULL; + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + +# if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +# ifndef __APPLE__ + if (flags & MMAP_MAP_32BIT) + map_flags |= MAP_32BIT; +# endif +# endif + + if (flags & MMAP_MAP_FIXED) + map_flags |= MAP_FIXED; + + addr = mmap(hint, request_size, map_prot, map_flags, -1, 0); + + if (addr == MAP_FAILED) + return NULL; + + return addr; +} + +static void* os_mmap_aligned(void* addr, + size_t requested_length, + int prot, + int flags, + size_t alignment, + size_t alignment_offset) +{ + size_t padded_length = requested_length + alignment + alignment_offset; + uintptr_t unaligned = (uintptr_t)os_mmap(addr, padded_length, prot, flags); + + if (!unaligned) { + return (void*)unaligned; + } + + // Round up the next address that has addr % alignment = 0 + const size_t alignment_corrected = alignment == 0 ? 1 : alignment; + uintptr_t aligned_nonoffset = + (unaligned + (alignment_corrected - 1)) & ~(alignment_corrected - 1); + + // Currently offset 0 is aligned according to alignment + // Alignment needs to be enforced at the given offset + uintptr_t aligned = 0; + if ((aligned_nonoffset - alignment_offset) >= unaligned) { + aligned = aligned_nonoffset - alignment_offset; + } else { + aligned = aligned_nonoffset - alignment_offset + alignment; + } + + // Sanity check + if (aligned < unaligned || + (aligned + (requested_length - 1)) > (unaligned + (padded_length - 1)) || + (aligned + alignment_offset) % alignment_corrected != 0) { + os_munmap((void*)unaligned, padded_length); + return NULL; + } + + { + size_t unused_front = aligned - unaligned; + if (unused_front != 0) { + os_munmap((void*)unaligned, unused_front); + } + } + + { + size_t unused_back = + (unaligned + (padded_length - 1)) - (aligned + (requested_length - 1)); + if (unused_back != 0) { + os_munmap((void*)(aligned + requested_length), unused_back); + } + } + + return (void*)aligned; +} + +static void os_munmap(void* addr, size_t size) +{ + uint64_t page_size = (uint64_t)os_getpagesize(); + uint64_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (munmap(addr, request_size)) { + printf("os_munmap error addr:%p, size:0x%" PRIx64 ", errno:%d\n", + addr, + request_size, + errno); + } + } +} + +static int os_mmap_commit(void* addr, size_t size, int prot) +{ + int map_prot = PROT_NONE; + uint64_t page_size = (uint64_t)os_getpagesize(); + uint64_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return 0; + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + + return mprotect(addr, request_size, map_prot); +} + +#else +# error "Unknown OS" +#endif |