Source code
Revision control
Copy as Markdown
Other Tools
#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
// 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