summaryrefslogtreecommitdiffstats
path: root/third_party/rlbox_wasm2c_sandbox
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rlbox_wasm2c_sandbox
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rlbox_wasm2c_sandbox')
-rwxr-xr-xthird_party/rlbox_wasm2c_sandbox/LICENSE21
-rwxr-xr-xthird_party/rlbox_wasm2c_sandbox/c_src/wasm2c_sandbox_wrapper.c7
-rw-r--r--third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp971
-rw-r--r--third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_tls.hpp33
-rw-r--r--third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_mem.h46
-rw-r--r--third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_minwasi.h38
-rw-r--r--third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_mem.c454
-rw-r--r--third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_minwasi.c799
8 files changed, 2369 insertions, 0 deletions
diff --git a/third_party/rlbox_wasm2c_sandbox/LICENSE b/third_party/rlbox_wasm2c_sandbox/LICENSE
new file mode 100755
index 0000000000..87e0ce55af
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 UCSD PLSysSec
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/rlbox_wasm2c_sandbox/c_src/wasm2c_sandbox_wrapper.c b/third_party/rlbox_wasm2c_sandbox/c_src/wasm2c_sandbox_wrapper.c
new file mode 100755
index 0000000000..99b1e7ec56
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/c_src/wasm2c_sandbox_wrapper.c
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+ (void) argc;
+ (void) argv;
+ abort();
+}
diff --git a/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp
new file mode 100644
index 0000000000..ed6975e27e
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_sandbox.hpp
@@ -0,0 +1,971 @@
+#pragma once
+
+#include "rlbox_wasm2c_tls.hpp"
+#include "wasm-rt.h"
+#include "wasm2c_rt_mem.h"
+#include "wasm2c_rt_minwasi.h"
+
+// Pull the helper header from the main repo for dynamic_check and scope_exit
+#include "rlbox_helpers.hpp"
+
+#include <cstdint>
+#include <iostream>
+#include <limits>
+#include <map>
+#include <memory>
+#include <mutex>
+// RLBox allows applications to provide a custom shared lock implementation
+#ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
+# include <shared_mutex>
+#endif
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#if defined(_WIN32)
+// Ensure the min/max macro in the header doesn't collide with functions in
+// std::
+# ifndef NOMINMAX
+# define NOMINMAX
+# endif
+# include <windows.h>
+#else
+# include <dlfcn.h>
+#endif
+
+#define RLBOX_WASM2C_UNUSED(...) (void)__VA_ARGS__
+
+// Use the same convention as rlbox to allow applications to customize the
+// shared lock
+#ifndef RLBOX_USE_CUSTOM_SHARED_LOCK
+# define RLBOX_SHARED_LOCK(name) std::shared_timed_mutex name
+# define RLBOX_ACQUIRE_SHARED_GUARD(name, ...) \
+ std::shared_lock<std::shared_timed_mutex> name(__VA_ARGS__)
+# define RLBOX_ACQUIRE_UNIQUE_GUARD(name, ...) \
+ std::unique_lock<std::shared_timed_mutex> name(__VA_ARGS__)
+#else
+# if !defined(RLBOX_SHARED_LOCK) || !defined(RLBOX_ACQUIRE_SHARED_GUARD) || \
+ !defined(RLBOX_ACQUIRE_UNIQUE_GUARD)
+# error \
+ "RLBOX_USE_CUSTOM_SHARED_LOCK defined but missing definitions for RLBOX_SHARED_LOCK, RLBOX_ACQUIRE_SHARED_GUARD, RLBOX_ACQUIRE_UNIQUE_GUARD"
+# endif
+#endif
+
+#define DEFINE_RLBOX_WASM2C_MODULE_TYPE(modname) \
+ struct rlbox_wasm2c_module_type_##modname \
+ { \
+ using instance_t = w2c_##modname; \
+ \
+ using create_instance_t = void (*)(instance_t*, \
+ struct w2c_env*, \
+ struct w2c_wasi__snapshot__preview1*); \
+ static constexpr create_instance_t create_instance = \
+ &wasm2c_##modname##_instantiate; \
+ \
+ using free_instance_t = void (*)(instance_t*); \
+ static constexpr free_instance_t free_instance = &wasm2c_##modname##_free; \
+ \
+ using get_func_type_t = wasm_rt_func_type_t (*)(uint32_t, uint32_t, ...); \
+ static constexpr get_func_type_t get_func_type = \
+ &wasm2c_##modname##_get_func_type; \
+ \
+ static constexpr const uint64_t* initial_memory_pages = \
+ &wasm2c_##modname##_min_env_memory; \
+ static constexpr const uint8_t* is_memory_64 = \
+ &wasm2c_##modname##_is64_env_memory; \
+ static constexpr const uint32_t* initial_func_elements = \
+ &wasm2c_##modname##_min_env_0x5F_indirect_function_table; \
+ \
+ static constexpr const char* prefix = #modname; \
+ \
+ /* A function that returns the address of the func specified as a \
+ * constexpr string */ \
+ /* Unfortunately, there is no way to implement the below in C++. */ \
+ /* Implement this to fully support multiple static modules. */ \
+ /* static constexpr void* dlsym_in_w2c_module(const char* func_name) { */ \
+ /* return &w2c_##modname##_%func%; */ \
+ /* } */ \
+ \
+ static constexpr auto malloc_address = &w2c_##modname##_malloc; \
+ static constexpr auto free_address = &w2c_##modname##_free; \
+ }
+
+// wasm_module_name module name used when compiling with wasm2c
+#ifndef RLBOX_WASM2C_MODULE_NAME
+# error "Expected definition for RLBOX_WASM2C_MODULE_NAME"
+#endif
+
+// Need an extra macro to expand RLBOX_WASM2C_MODULE_NAME
+#define INVOKE_DEFINE_RLBOX_WASM2C_MODULE_TYPE(modname) \
+ DEFINE_RLBOX_WASM2C_MODULE_TYPE(modname)
+
+INVOKE_DEFINE_RLBOX_WASM2C_MODULE_TYPE(RLBOX_WASM2C_MODULE_NAME);
+
+// Concat after macro expansion
+#define RLBOX_WASM2C_CONCAT2(x, y) x##y
+#define RLBOX_WASM2C_CONCAT(x, y) RLBOX_WASM2C_CONCAT2(x, y)
+
+#define RLBOX_WASM_MODULE_TYPE_CURR \
+ RLBOX_WASM2C_CONCAT(rlbox_wasm2c_module_type_, RLBOX_WASM2C_MODULE_NAME)
+
+#define RLBOX_WASM2C_STRINGIFY(x) RLBOX_WASM2C_STRINGIFY2(x)
+#define RLBOX_WASM2C_STRINGIFY2(x) #x
+
+#define RLBOX_WASM2C_MODULE_NAME_STR \
+ RLBOX_WASM2C_STRINGIFY(RLBOX_WASM2C_MODULE_NAME)
+
+#define RLBOX_WASM2C_MODULE_FUNC_HELPER2(part1, part2, part3) \
+ part1##part2##part3
+#define RLBOX_WASM2C_MODULE_FUNC_HELPER(part1, part2, part3) \
+ RLBOX_WASM2C_MODULE_FUNC_HELPER2(part1, part2, part3)
+#define RLBOX_WASM2C_MODULE_FUNC(name) \
+ RLBOX_WASM2C_MODULE_FUNC_HELPER(w2c_, RLBOX_WASM2C_MODULE_NAME, name)
+
+namespace rlbox {
+
+namespace wasm2c_detail {
+
+ template<typename T>
+ constexpr bool false_v = false;
+
+ // https://stackoverflow.com/questions/6512019/can-we-get-the-type-of-a-lambda-argument
+ namespace return_argument_detail {
+ template<typename Ret, typename... Rest>
+ Ret helper(Ret (*)(Rest...));
+
+ template<typename Ret, typename F, typename... Rest>
+ Ret helper(Ret (F::*)(Rest...));
+
+ template<typename Ret, typename F, typename... Rest>
+ Ret helper(Ret (F::*)(Rest...) const);
+
+ template<typename F>
+ decltype(helper(&F::operator())) helper(F);
+ } // namespace return_argument_detail
+
+ template<typename T>
+ using return_argument =
+ decltype(return_argument_detail::helper(std::declval<T>()));
+
+ ///////////////////////////////////////////////////////////////
+
+ // https://stackoverflow.com/questions/37602057/why-isnt-a-for-loop-a-compile-time-expression
+ namespace compile_time_for_detail {
+ template<std::size_t N>
+ struct num
+ {
+ static const constexpr auto value = N;
+ };
+
+ template<class F, std::size_t... Is>
+ inline void compile_time_for_helper(F func, std::index_sequence<Is...>)
+ {
+ (func(num<Is>{}), ...);
+ }
+ } // namespace compile_time_for_detail
+
+ template<std::size_t N, typename F>
+ inline void compile_time_for(F func)
+ {
+ compile_time_for_detail::compile_time_for_helper(
+ func, std::make_index_sequence<N>());
+ }
+
+ ///////////////////////////////////////////////////////////////
+
+ template<typename T, typename = void>
+ struct convert_type_to_wasm_type
+ {
+ static_assert(std::is_void_v<T>, "Missing specialization");
+ using type = void;
+ // wasm2c has no void type so use i32 for now
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_I32;
+ };
+
+ template<typename T>
+ struct convert_type_to_wasm_type<
+ T,
+ std::enable_if_t<(std::is_integral_v<T> || std::is_enum_v<T>)&&sizeof(T) <=
+ sizeof(uint32_t)>>
+ {
+ using type = uint32_t;
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_I32;
+ };
+
+ template<typename T>
+ struct convert_type_to_wasm_type<
+ T,
+ std::enable_if_t<(std::is_integral_v<T> ||
+ std::is_enum_v<T>)&&sizeof(uint32_t) < sizeof(T) &&
+ sizeof(T) <= sizeof(uint64_t)>>
+ {
+ using type = uint64_t;
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_I64;
+ };
+
+ template<typename T>
+ struct convert_type_to_wasm_type<T,
+ std::enable_if_t<std::is_same_v<T, float>>>
+ {
+ using type = T;
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_F32;
+ };
+
+ template<typename T>
+ struct convert_type_to_wasm_type<T,
+ std::enable_if_t<std::is_same_v<T, double>>>
+ {
+ using type = T;
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_F64;
+ };
+
+ template<typename T>
+ struct convert_type_to_wasm_type<
+ T,
+ std::enable_if_t<std::is_pointer_v<T> || std::is_class_v<T>>>
+ {
+ // pointers are 32 bit indexes in wasm
+ // class paramters are passed as a pointer to an object in the stack or heap
+ using type = uint32_t;
+ static constexpr wasm_rt_type_t wasm2c_type = WASM_RT_I32;
+ };
+
+ ///////////////////////////////////////////////////////////////
+
+ namespace prepend_arg_type_detail {
+ template<typename T, typename T_ArgNew>
+ struct helper;
+
+ template<typename T_ArgNew, typename T_Ret, typename... T_Args>
+ struct helper<T_Ret(T_Args...), T_ArgNew>
+ {
+ using type = T_Ret(T_ArgNew, T_Args...);
+ };
+ }
+
+ template<typename T_Func, typename T_ArgNew>
+ using prepend_arg_type =
+ typename prepend_arg_type_detail::helper<T_Func, T_ArgNew>::type;
+
+ ///////////////////////////////////////////////////////////////
+
+ namespace change_return_type_detail {
+ template<typename T, typename T_RetNew>
+ struct helper;
+
+ template<typename T_RetNew, typename T_Ret, typename... T_Args>
+ struct helper<T_Ret(T_Args...), T_RetNew>
+ {
+ using type = T_RetNew(T_Args...);
+ };
+ }
+
+ template<typename T_Func, typename T_RetNew>
+ using change_return_type =
+ typename change_return_type_detail::helper<T_Func, T_RetNew>::type;
+
+ ///////////////////////////////////////////////////////////////
+
+ namespace change_class_arg_types_detail {
+ template<typename T, typename T_ArgNew>
+ struct helper;
+
+ template<typename T_ArgNew, typename T_Ret, typename... T_Args>
+ struct helper<T_Ret(T_Args...), T_ArgNew>
+ {
+ using type =
+ T_Ret(std::conditional_t<std::is_class_v<T_Args>, T_ArgNew, T_Args>...);
+ };
+ }
+
+ template<typename T_Func, typename T_ArgNew>
+ using change_class_arg_types =
+ typename change_class_arg_types_detail::helper<T_Func, T_ArgNew>::type;
+
+} // namespace wasm2c_detail
+
+// declare the static symbol with weak linkage to keep this header only
+#if defined(_MSC_VER)
+__declspec(selectany)
+#else
+__attribute__((weak))
+#endif
+ std::once_flag rlbox_wasm2c_initialized;
+
+class rlbox_wasm2c_sandbox
+{
+public:
+ using T_LongLongType = int64_t;
+ using T_LongType = int32_t;
+ using T_IntType = int32_t;
+ using T_PointerType = uint32_t;
+ using T_ShortType = int16_t;
+
+private:
+ mutable typename RLBOX_WASM_MODULE_TYPE_CURR::instance_t wasm2c_instance{ 0 };
+ struct w2c_env sandbox_memory_env;
+ struct w2c_wasi__snapshot__preview1 wasi_env;
+ bool instance_initialized = false;
+ wasm_rt_memory_t sandbox_memory_info;
+ mutable wasm_rt_funcref_table_t sandbox_callback_table;
+ uintptr_t heap_base;
+ size_t return_slot_size = 0;
+ T_PointerType return_slot = 0;
+ mutable std::vector<T_PointerType> callback_free_list;
+
+ static const size_t MAX_CALLBACKS = 128;
+ mutable RLBOX_SHARED_LOCK(callback_mutex);
+ void* callback_unique_keys[MAX_CALLBACKS]{ 0 };
+ void* callbacks[MAX_CALLBACKS]{ 0 };
+ uint32_t callback_slot_assignment[MAX_CALLBACKS]{ 0 };
+ mutable std::map<const void*, uint32_t> internal_callbacks;
+ mutable std::map<uint32_t, const void*> slot_assignments;
+
+#ifndef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+ thread_local static inline rlbox_wasm2c_sandbox_thread_data thread_data{ 0,
+ 0 };
+#endif
+
+ template<typename T_FormalRet, typename T_ActualRet>
+ inline auto serialize_to_sandbox(T_ActualRet arg)
+ {
+ if constexpr (std::is_class_v<T_FormalRet>) {
+ // structs returned as pointers into wasm memory/wasm stack
+ auto ptr = reinterpret_cast<T_FormalRet*>(
+ impl_get_unsandboxed_pointer<T_FormalRet*>(arg));
+ T_FormalRet ret = *ptr;
+ return ret;
+ } else {
+ return arg;
+ }
+ }
+
+ template<uint32_t N, typename T_Ret, typename... T_Args>
+ static typename wasm2c_detail::convert_type_to_wasm_type<T_Ret>::type
+ callback_interceptor(
+ void* /* vmContext */,
+ typename wasm2c_detail::convert_type_to_wasm_type<T_Args>::type... params)
+ {
+#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+ auto& thread_data = *get_rlbox_wasm2c_sandbox_thread_data();
+#endif
+ thread_data.last_callback_invoked = N;
+ using T_Func = T_Ret (*)(T_Args...);
+ T_Func func;
+ {
+#ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
+ RLBOX_ACQUIRE_SHARED_GUARD(lock, thread_data.sandbox->callback_mutex);
+#endif
+ func = reinterpret_cast<T_Func>(thread_data.sandbox->callbacks[N]);
+ }
+ // Callbacks are invoked through function pointers, cannot use std::forward
+ // as we don't have caller context for T_Args, which means they are all
+ // effectively passed by value
+ return func(
+ thread_data.sandbox->template serialize_to_sandbox<T_Args>(params)...);
+ }
+
+ template<uint32_t N, typename T_Ret, typename... T_Args>
+ static void callback_interceptor_promoted(
+ void* /* vmContext */,
+ typename wasm2c_detail::convert_type_to_wasm_type<T_Ret>::type ret,
+ typename wasm2c_detail::convert_type_to_wasm_type<T_Args>::type... params)
+ {
+#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+ auto& thread_data = *get_rlbox_wasm2c_sandbox_thread_data();
+#endif
+ thread_data.last_callback_invoked = N;
+ using T_Func = T_Ret (*)(T_Args...);
+ T_Func func;
+ {
+#ifndef RLBOX_SINGLE_THREADED_INVOCATIONS
+ RLBOX_ACQUIRE_SHARED_GUARD(lock, thread_data.sandbox->callback_mutex);
+#endif
+ func = reinterpret_cast<T_Func>(thread_data.sandbox->callbacks[N]);
+ }
+ // Callbacks are invoked through function pointers, cannot use std::forward
+ // as we don't have caller context for T_Args, which means they are all
+ // effectively passed by value
+ auto ret_val = func(
+ thread_data.sandbox->template serialize_to_sandbox<T_Args>(params)...);
+ // Copy the return value back
+ auto ret_ptr = reinterpret_cast<T_Ret*>(
+ thread_data.sandbox->template impl_get_unsandboxed_pointer<T_Ret*>(ret));
+ *ret_ptr = ret_val;
+ }
+
+ template<typename T_Ret, typename... T_Args>
+ inline wasm_rt_func_type_t get_wasm2c_func_index(
+ // dummy for template inference
+ T_Ret (*)(T_Args...) = nullptr) const
+ {
+ // Class return types as promoted to args
+ constexpr bool promoted = std::is_class_v<T_Ret>;
+ constexpr uint32_t param_count =
+ promoted ? (sizeof...(T_Args) + 1) : (sizeof...(T_Args));
+ constexpr uint32_t ret_count =
+ promoted ? 0 : (std::is_void_v<T_Ret> ? 0 : 1);
+
+ wasm_rt_func_type_t ret = nullptr;
+ if constexpr (ret_count == 0) {
+ ret = RLBOX_WASM_MODULE_TYPE_CURR::get_func_type(
+ param_count,
+ ret_count,
+ wasm2c_detail::convert_type_to_wasm_type<T_Args>::wasm2c_type...);
+ } else {
+ ret = RLBOX_WASM_MODULE_TYPE_CURR::get_func_type(
+ param_count,
+ ret_count,
+ wasm2c_detail::convert_type_to_wasm_type<T_Args>::wasm2c_type...,
+ wasm2c_detail::convert_type_to_wasm_type<T_Ret>::wasm2c_type);
+ }
+
+ return ret;
+ }
+
+ void ensure_return_slot_size(size_t size)
+ {
+ if (size > return_slot_size) {
+ if (return_slot_size) {
+ impl_free_in_sandbox(return_slot);
+ }
+ return_slot = impl_malloc_in_sandbox(size);
+ detail::dynamic_check(
+ return_slot != 0,
+ "Error initializing return slot. Sandbox may be out of memory!");
+ return_slot_size = size;
+ }
+ }
+
+ // function takes a 32-bit value and returns the next power of 2
+ // return is a 64-bit value as large 32-bit values will return 2^32
+ static inline uint64_t next_power_of_two(uint32_t value)
+ {
+ uint64_t power = 1;
+ while (power < value) {
+ power *= 2;
+ }
+ return power;
+ }
+
+protected:
+#define rlbox_wasm2c_sandbox_lookup_symbol(func_name) \
+ reinterpret_cast<void*>(&RLBOX_WASM2C_MODULE_FUNC(_##func_name)) /* NOLINT \
+ */
+
+ // adding a template so that we can use static_assert to fire only if this
+ // function is invoked
+ template<typename T = void>
+ void* impl_lookup_symbol(const char* func_name)
+ {
+ constexpr bool fail = std::is_same_v<T, void>;
+ static_assert(
+ !fail,
+ "The wasm2c_sandbox uses static calls and thus developers should add\n\n"
+ "#define RLBOX_USE_STATIC_CALLS() rlbox_wasm2c_sandbox_lookup_symbol\n\n"
+ "to their code, to ensure that static calls are handled correctly.");
+ return nullptr;
+ }
+
+public:
+#define FALLIBLE_DYNAMIC_CHECK(infallible, cond, msg) \
+ if (infallible) { \
+ detail::dynamic_check(cond, msg); \
+ } else if (!(cond)) { \
+ impl_destroy_sandbox(); \
+ return false; \
+ }
+
+ /**
+ * @brief creates the Wasm sandbox from the given shared library
+ *
+ * @param infallible if set to true, the sandbox aborts on failure. If false,
+ * the sandbox returns creation status as a return value
+ * @param custom_capacity allows optionally overriding the platform-specified
+ * maximum size of the wasm heap allowed for this sandbox instance.
+ * @return true when sandbox is successfully created. false when infallible is
+ * set to false and sandbox was not successfully created. If infallible is set
+ * to true, this function will never return false.
+ */
+ inline bool impl_create_sandbox(
+ bool infallible = true,
+ const w2c_mem_capacity* custom_capacity = nullptr)
+ {
+ FALLIBLE_DYNAMIC_CHECK(
+ infallible, instance_initialized == false, "Sandbox already initialized");
+
+ bool minwasi_init_succeeded = true;
+
+ std::call_once(rlbox_wasm2c_initialized, [&]() {
+ wasm_rt_init();
+ minwasi_init_succeeded = minwasi_init();
+ });
+
+ FALLIBLE_DYNAMIC_CHECK(
+ infallible, minwasi_init_succeeded, "Could not initialize min wasi");
+
+ const bool minwasi_init_inst_succeeded = minwasi_init_instance(&wasi_env);
+ FALLIBLE_DYNAMIC_CHECK(
+ infallible, minwasi_init_inst_succeeded, "Could not initialize min wasi instance");
+
+ if (custom_capacity) {
+ FALLIBLE_DYNAMIC_CHECK(
+ infallible, custom_capacity->is_valid, "Invalid capacity");
+ }
+
+ sandbox_memory_info = create_wasm2c_memory(
+ *RLBOX_WASM_MODULE_TYPE_CURR::initial_memory_pages, custom_capacity);
+ FALLIBLE_DYNAMIC_CHECK(infallible,
+ sandbox_memory_info.data != nullptr,
+ "Could not allocate a heap for the wasm2c sandbox");
+
+ FALLIBLE_DYNAMIC_CHECK(infallible,
+ *RLBOX_WASM_MODULE_TYPE_CURR::is_memory_64 == 0,
+ "Does not support Wasm with memory64");
+
+ const uint32_t max_table_size = 0xffffffffu; /* this means unlimited */
+ wasm_rt_allocate_funcref_table(
+ &sandbox_callback_table,
+ *RLBOX_WASM_MODULE_TYPE_CURR::initial_func_elements,
+ max_table_size);
+
+ sandbox_memory_env.sandbox_memory_info = &sandbox_memory_info;
+ sandbox_memory_env.sandbox_callback_table = &sandbox_callback_table;
+ wasi_env.instance_memory = &sandbox_memory_info;
+ RLBOX_WASM_MODULE_TYPE_CURR::create_instance(
+ &wasm2c_instance, &sandbox_memory_env, &wasi_env);
+
+ heap_base = reinterpret_cast<uintptr_t>(impl_get_memory_location());
+
+ if constexpr (sizeof(uintptr_t) != sizeof(uint32_t)) {
+ // On larger platforms, check that the heap is aligned to the pointer size
+ // i.e. 32-bit pointer => aligned to 4GB. The implementations of
+ // impl_get_unsandboxed_pointer_no_ctx and
+ // impl_get_sandboxed_pointer_no_ctx below rely on this.
+ uintptr_t heap_offset_mask = std::numeric_limits<T_PointerType>::max();
+ FALLIBLE_DYNAMIC_CHECK(infallible,
+ (heap_base & heap_offset_mask) == 0,
+ "Sandbox heap not aligned to 4GB");
+ }
+
+ instance_initialized = true;
+
+ return true;
+ }
+
+#undef FALLIBLE_DYNAMIC_CHECK
+
+ inline void impl_destroy_sandbox()
+ {
+ if (return_slot_size) {
+ impl_free_in_sandbox(return_slot);
+ }
+
+ if (instance_initialized) {
+ instance_initialized = false;
+ RLBOX_WASM_MODULE_TYPE_CURR::free_instance(&wasm2c_instance);
+ }
+
+ destroy_wasm2c_memory(&sandbox_memory_info);
+ wasm_rt_free_funcref_table(&sandbox_callback_table);
+ minwasi_cleanup_instance(&wasi_env);
+ }
+
+ template<typename T>
+ inline void* impl_get_unsandboxed_pointer(T_PointerType p) const
+ {
+ if constexpr (std::is_function_v<std::remove_pointer_t<T>>) {
+ RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
+ auto found = slot_assignments.find(p);
+ if (found != slot_assignments.end()) {
+ auto ret = found->second;
+ return const_cast<void*>(ret);
+ } else {
+ return nullptr;
+ }
+ } else {
+ return reinterpret_cast<void*>(heap_base + p);
+ }
+ }
+
+ template<typename T>
+ inline T_PointerType impl_get_sandboxed_pointer(const void* p) const
+ {
+ if constexpr (std::is_function_v<std::remove_pointer_t<T>>) {
+ RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
+
+ uint32_t slot_number = 0;
+ auto found = internal_callbacks.find(p);
+ if (found != internal_callbacks.end()) {
+ slot_number = found->second;
+ } else {
+
+ slot_number = new_callback_slot();
+ wasm_rt_funcref_t func_val;
+ func_val.func_type = get_wasm2c_func_index(static_cast<T>(nullptr));
+ func_val.func =
+ reinterpret_cast<wasm_rt_function_ptr_t>(const_cast<void*>(p));
+ func_val.module_instance = &wasm2c_instance;
+
+ sandbox_callback_table.data[slot_number] = func_val;
+ internal_callbacks[p] = slot_number;
+ slot_assignments[slot_number] = p;
+ }
+ return static_cast<T_PointerType>(slot_number);
+ } else {
+ if constexpr (sizeof(uintptr_t) == sizeof(uint32_t)) {
+ return static_cast<T_PointerType>(reinterpret_cast<uintptr_t>(p) -
+ heap_base);
+ } else {
+ return static_cast<T_PointerType>(reinterpret_cast<uintptr_t>(p));
+ }
+ }
+ }
+
+ template<typename T>
+ static inline void* impl_get_unsandboxed_pointer_no_ctx(
+ T_PointerType p,
+ const void* example_unsandboxed_ptr,
+ rlbox_wasm2c_sandbox* (*expensive_sandbox_finder)(
+ const void* example_unsandboxed_ptr))
+ {
+ // on 32-bit platforms we don't assume the heap is aligned
+ if constexpr (sizeof(uintptr_t) == sizeof(uint32_t)) {
+ auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr);
+ return sandbox->template impl_get_unsandboxed_pointer<T>(p);
+ } else {
+ if constexpr (std::is_function_v<std::remove_pointer_t<T>>) {
+ // swizzling function pointers needs access to the function pointer
+ // tables and thus cannot be done without context
+ auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr);
+ return sandbox->template impl_get_unsandboxed_pointer<T>(p);
+ } else {
+ // grab the memory base from the example_unsandboxed_ptr
+ uintptr_t heap_base_mask =
+ std::numeric_limits<uintptr_t>::max() &
+ ~(static_cast<uintptr_t>(std::numeric_limits<T_PointerType>::max()));
+ uintptr_t computed_heap_base =
+ reinterpret_cast<uintptr_t>(example_unsandboxed_ptr) & heap_base_mask;
+ uintptr_t ret = computed_heap_base | p;
+ return reinterpret_cast<void*>(ret);
+ }
+ }
+ }
+
+ template<typename T>
+ static inline T_PointerType impl_get_sandboxed_pointer_no_ctx(
+ const void* p,
+ const void* example_unsandboxed_ptr,
+ rlbox_wasm2c_sandbox* (*expensive_sandbox_finder)(
+ const void* example_unsandboxed_ptr))
+ {
+ // on 32-bit platforms we don't assume the heap is aligned
+ if constexpr (sizeof(uintptr_t) == sizeof(uint32_t)) {
+ auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr);
+ return sandbox->template impl_get_sandboxed_pointer<T>(p);
+ } else {
+ if constexpr (std::is_function_v<std::remove_pointer_t<T>>) {
+ // swizzling function pointers needs access to the function pointer
+ // tables and thus cannot be done without context
+ auto sandbox = expensive_sandbox_finder(example_unsandboxed_ptr);
+ return sandbox->template impl_get_sandboxed_pointer<T>(p);
+ } else {
+ // Just clear the memory base to leave the offset
+ RLBOX_WASM2C_UNUSED(example_unsandboxed_ptr);
+ uintptr_t ret = reinterpret_cast<uintptr_t>(p) &
+ std::numeric_limits<T_PointerType>::max();
+ return static_cast<T_PointerType>(ret);
+ }
+ }
+ }
+
+ static inline bool impl_is_in_same_sandbox(const void* p1, const void* p2)
+ {
+ uintptr_t heap_base_mask = std::numeric_limits<uintptr_t>::max() &
+ ~(std::numeric_limits<T_PointerType>::max());
+ return (reinterpret_cast<uintptr_t>(p1) & heap_base_mask) ==
+ (reinterpret_cast<uintptr_t>(p2) & heap_base_mask);
+ }
+
+ inline bool impl_is_pointer_in_sandbox_memory(const void* p)
+ {
+ size_t length = impl_get_total_memory();
+ uintptr_t p_val = reinterpret_cast<uintptr_t>(p);
+ return p_val >= heap_base && p_val < (heap_base + length);
+ }
+
+ inline bool impl_is_pointer_in_app_memory(const void* p)
+ {
+ return !(impl_is_pointer_in_sandbox_memory(p));
+ }
+
+ inline size_t impl_get_total_memory() { return sandbox_memory_info.size; }
+
+ inline void* impl_get_memory_location() const
+ {
+ return sandbox_memory_info.data;
+ }
+
+ template<typename T, typename T_Converted, typename... T_Args>
+ auto impl_invoke_with_func_ptr(T_Converted* func_ptr, T_Args&&... params)
+ {
+#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+ auto& thread_data = *get_rlbox_wasm2c_sandbox_thread_data();
+#endif
+ auto old_sandbox = thread_data.sandbox;
+ thread_data.sandbox = this;
+ auto on_exit =
+ detail::make_scope_exit([&] { thread_data.sandbox = old_sandbox; });
+
+ // WASM functions are mangled in the following manner
+ // 1. All primitive types are left as is and follow an LP32 machine model
+ // (as opposed to the possibly 64-bit application)
+ // 2. All pointers are changed to u32 types
+ // 3. Returned class are returned as an out parameter before the actual
+ // function parameters
+ // 4. All class parameters are passed as pointers (u32 types)
+ // 5. The heap address is passed in as the first argument to the function
+ //
+ // RLBox accounts for the first 2 differences in T_Converted type, but we
+ // need to handle the rest
+
+ // Handle point 3
+ using T_Ret = wasm2c_detail::return_argument<T_Converted>;
+ if constexpr (std::is_class_v<T_Ret>) {
+ using T_Conv1 = wasm2c_detail::change_return_type<T_Converted, void>;
+ using T_Conv2 = wasm2c_detail::prepend_arg_type<T_Conv1, T_PointerType>;
+ auto func_ptr_conv =
+ reinterpret_cast<T_Conv2*>(reinterpret_cast<uintptr_t>(func_ptr));
+ ensure_return_slot_size(sizeof(T_Ret));
+ impl_invoke_with_func_ptr<T>(func_ptr_conv, return_slot, params...);
+
+ auto ptr = reinterpret_cast<T_Ret*>(
+ impl_get_unsandboxed_pointer<T_Ret*>(return_slot));
+ T_Ret ret = *ptr;
+ return ret;
+ }
+
+ // Handle point 4
+ constexpr size_t alloc_length = [&] {
+ if constexpr (sizeof...(params) > 0) {
+ return ((std::is_class_v<T_Args> ? 1 : 0) + ...);
+ } else {
+ return 0;
+ }
+ }();
+
+ // 0 arg functions create 0 length arrays which is not allowed
+ T_PointerType allocations_buff[alloc_length == 0 ? 1 : alloc_length];
+ T_PointerType* allocations = allocations_buff;
+
+ auto serialize_class_arg =
+ [&](auto arg) -> std::conditional_t<std::is_class_v<decltype(arg)>,
+ T_PointerType,
+ decltype(arg)> {
+ using T_Arg = decltype(arg);
+ if constexpr (std::is_class_v<T_Arg>) {
+ auto slot = impl_malloc_in_sandbox(sizeof(T_Arg));
+ auto ptr =
+ reinterpret_cast<T_Arg*>(impl_get_unsandboxed_pointer<T_Arg*>(slot));
+ *ptr = arg;
+ allocations[0] = slot;
+ allocations++;
+ return slot;
+ } else {
+ return arg;
+ }
+ };
+
+ // 0 arg functions don't use serialize
+ RLBOX_WASM2C_UNUSED(serialize_class_arg);
+
+ using T_ConvNoClass =
+ wasm2c_detail::change_class_arg_types<T_Converted, T_PointerType>;
+
+ // Handle Point 5
+ using T_ConvHeap = wasm2c_detail::prepend_arg_type<
+ T_ConvNoClass,
+ typename RLBOX_WASM_MODULE_TYPE_CURR::instance_t*>;
+
+ // Function invocation
+ auto func_ptr_conv =
+ reinterpret_cast<T_ConvHeap*>(reinterpret_cast<uintptr_t>(func_ptr));
+
+ using T_NoVoidRet =
+ std::conditional_t<std::is_void_v<T_Ret>, uint32_t, T_Ret>;
+ T_NoVoidRet ret;
+
+ if constexpr (std::is_void_v<T_Ret>) {
+ RLBOX_WASM2C_UNUSED(ret);
+ func_ptr_conv(&wasm2c_instance, serialize_class_arg(params)...);
+ } else {
+ ret = func_ptr_conv(&wasm2c_instance, serialize_class_arg(params)...);
+ }
+
+ for (size_t i = 0; i < alloc_length; i++) {
+ impl_free_in_sandbox(allocations_buff[i]);
+ }
+
+ if constexpr (!std::is_void_v<T_Ret>) {
+ return ret;
+ }
+ }
+
+ inline T_PointerType impl_malloc_in_sandbox(size_t size)
+ {
+ if constexpr (sizeof(size) > sizeof(uint32_t)) {
+ detail::dynamic_check(size <= std::numeric_limits<uint32_t>::max(),
+ "Attempting to malloc more than the heap size");
+ }
+ using T_Func = void*(size_t);
+ using T_Converted = T_PointerType(uint32_t);
+ T_PointerType ret = impl_invoke_with_func_ptr<T_Func, T_Converted>(
+ reinterpret_cast<T_Converted*>(
+ RLBOX_WASM_MODULE_TYPE_CURR::malloc_address),
+ static_cast<uint32_t>(size));
+ return ret;
+ }
+
+ inline void impl_free_in_sandbox(T_PointerType p)
+ {
+ using T_Func = void(void*);
+ using T_Converted = void(T_PointerType);
+ impl_invoke_with_func_ptr<T_Func, T_Converted>(
+ reinterpret_cast<T_Converted*>(RLBOX_WASM_MODULE_TYPE_CURR::free_address),
+ p);
+ }
+
+private:
+ // Should be called with callback_mutex held
+ uint32_t new_callback_slot() const
+ {
+ if (callback_free_list.size() > 0) {
+ uint32_t ret = callback_free_list.back();
+ callback_free_list.pop_back();
+ return ret;
+ }
+
+ const uint32_t curr_size = sandbox_callback_table.size;
+
+ detail::dynamic_check(
+ curr_size < sandbox_callback_table.max_size,
+ "Could not find an empty row in Wasm instance table. This would "
+ "happen if you have registered too many callbacks, or unsandboxed "
+ "too many function pointers.");
+
+ wasm_rt_funcref_t func_val{ 0 };
+ // on success, this returns the previous number of elements in the table
+ const uint32_t ret =
+ wasm_rt_grow_funcref_table(&sandbox_callback_table, 1, func_val);
+
+ detail::dynamic_check(
+ ret != 0 && ret != (uint32_t)-1,
+ "Adding a new callback slot to the wasm instance failed.");
+
+ // We have expanded the number of slots
+ // Previous slots size: ret
+ // New slot is at index: ret
+ const uint32_t slot_number = ret;
+ return slot_number;
+ }
+
+ void free_callback_slot(uint32_t slot) const
+ {
+ callback_free_list.push_back(slot);
+ }
+
+public:
+ template<typename T_Ret, typename... T_Args>
+ inline T_PointerType impl_register_callback(void* key, void* callback)
+ {
+ bool found = false;
+ uint32_t found_loc = 0;
+ wasm_rt_function_ptr_t chosen_interceptor = nullptr;
+
+ RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
+
+ // need a compile time for loop as we we need I to be a compile time value
+ // this is because we are setting the I'th callback ineterceptor
+ wasm2c_detail::compile_time_for<MAX_CALLBACKS>([&](auto I) {
+ constexpr auto i = I.value;
+ if (!found && callbacks[i] == nullptr) {
+ found = true;
+ found_loc = i;
+
+ if constexpr (std::is_class_v<T_Ret>) {
+ chosen_interceptor = (wasm_rt_function_ptr_t)(
+ callback_interceptor_promoted<i, T_Ret, T_Args...>);
+ } else {
+ chosen_interceptor =
+ (wasm_rt_function_ptr_t)(callback_interceptor<i, T_Ret, T_Args...>);
+ }
+ }
+ });
+
+ detail::dynamic_check(
+ found,
+ "Could not find an empty slot in sandbox function table. This would "
+ "happen if you have registered too many callbacks, or unsandboxed "
+ "too many function pointers. You can file a bug if you want to "
+ "increase the maximum allowed callbacks or unsadnboxed functions "
+ "pointers");
+
+ wasm_rt_funcref_t func_val;
+ func_val.func_type = get_wasm2c_func_index<T_Ret, T_Args...>();
+ func_val.func = chosen_interceptor;
+ func_val.module_instance = &wasm2c_instance;
+
+ const uint32_t slot_number = new_callback_slot();
+ sandbox_callback_table.data[slot_number] = func_val;
+
+ callback_unique_keys[found_loc] = key;
+ callbacks[found_loc] = callback;
+ callback_slot_assignment[found_loc] = slot_number;
+ slot_assignments[slot_number] = callback;
+
+ return static_cast<T_PointerType>(slot_number);
+ }
+
+ static inline std::pair<rlbox_wasm2c_sandbox*, void*>
+ impl_get_executed_callback_sandbox_and_key()
+ {
+#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+ auto& thread_data = *get_rlbox_wasm2c_sandbox_thread_data();
+#endif
+ auto sandbox = thread_data.sandbox;
+ auto callback_num = thread_data.last_callback_invoked;
+ void* key = sandbox->callback_unique_keys[callback_num];
+ return std::make_pair(sandbox, key);
+ }
+
+ template<typename T_Ret, typename... T_Args>
+ inline void impl_unregister_callback(void* key)
+ {
+ bool found = false;
+ uint32_t i = 0;
+ {
+ RLBOX_ACQUIRE_UNIQUE_GUARD(lock, callback_mutex);
+ for (; i < MAX_CALLBACKS; i++) {
+ if (callback_unique_keys[i] == key) {
+ const uint32_t slot_number = callback_slot_assignment[i];
+ wasm_rt_funcref_t func_val{ 0 };
+ sandbox_callback_table.data[slot_number] = func_val;
+
+ callback_unique_keys[i] = nullptr;
+ callbacks[i] = nullptr;
+ callback_slot_assignment[i] = 0;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ detail::dynamic_check(
+ found, "Internal error: Could not find callback to unregister");
+
+ return;
+ }
+};
+
+} // namespace rlbox
diff --git a/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_tls.hpp b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_tls.hpp
new file mode 100644
index 0000000000..b87ac7bf5f
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/include/rlbox_wasm2c_tls.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <stdint.h>
+
+namespace rlbox {
+
+class rlbox_wasm2c_sandbox;
+
+struct rlbox_wasm2c_sandbox_thread_data
+{
+ rlbox_wasm2c_sandbox* sandbox;
+ uint32_t last_callback_invoked;
+};
+
+#ifdef RLBOX_EMBEDDER_PROVIDES_TLS_STATIC_VARIABLES
+
+rlbox_wasm2c_sandbox_thread_data* get_rlbox_wasm2c_sandbox_thread_data();
+
+# define RLBOX_WASM2C_SANDBOX_STATIC_VARIABLES() \
+ thread_local rlbox::rlbox_wasm2c_sandbox_thread_data \
+ rlbox_wasm2c_sandbox_thread_info{ 0, 0 }; \
+ \
+ namespace rlbox { \
+ rlbox_wasm2c_sandbox_thread_data* get_rlbox_wasm2c_sandbox_thread_data() \
+ { \
+ return &rlbox_wasm2c_sandbox_thread_info; \
+ } \
+ } \
+ static_assert(true, "Enforce semi-colon")
+
+#endif
+
+} // namespace rlbox
diff --git a/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_mem.h b/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_mem.h
new file mode 100644
index 0000000000..e61b9fa3eb
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_mem.h
@@ -0,0 +1,46 @@
+#ifndef WASM_RT_OS_H_
+#define WASM_RT_OS_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "wasm-rt.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct w2c_env
+ {
+ wasm_rt_memory_t* sandbox_memory_info;
+ wasm_rt_funcref_table_t* sandbox_callback_table;
+ } w2c_env;
+
+ wasm_rt_memory_t* w2c_env_memory(struct w2c_env* instance);
+ wasm_rt_funcref_table_t* w2c_env_0x5F_indirect_function_table(
+ struct w2c_env*);
+
+ typedef struct w2c_mem_capacity
+ {
+ bool is_valid;
+ bool is_mem_32;
+ uint64_t max_pages;
+ uint64_t max_size;
+ } w2c_mem_capacity;
+
+ w2c_mem_capacity get_valid_wasm2c_memory_capacity(uint64_t min_capacity,
+ bool is_mem_32);
+
+ wasm_rt_memory_t create_wasm2c_memory(
+ uint32_t initial_pages,
+ const w2c_mem_capacity* custom_capacity);
+ void destroy_wasm2c_memory(wasm_rt_memory_t* memory);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_minwasi.h b/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_minwasi.h
new file mode 100644
index 0000000000..51542b5d1e
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/include/wasm2c_rt_minwasi.h
@@ -0,0 +1,38 @@
+#ifndef WASM_RT_MINWASI_H_
+#define WASM_RT_MINWASI_H_
+
+/* A minimum wasi implementation supporting only stdin, stdout, stderr, argv
+ * (upto 100 args) and clock functions. */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "wasm-rt.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ typedef struct w2c_wasi__snapshot__preview1
+ {
+ wasm_rt_memory_t* instance_memory;
+
+ uint32_t main_argc;
+ const char** main_argv;
+
+ uint32_t env_count;
+ const char** env;
+
+ void* clock_data;
+ } w2c_wasi__snapshot__preview1;
+
+ bool minwasi_init();
+ bool minwasi_init_instance(w2c_wasi__snapshot__preview1* wasi_data);
+ void minwasi_cleanup_instance(w2c_wasi__snapshot__preview1* wasi_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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
diff --git a/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_minwasi.c b/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_minwasi.c
new file mode 100644
index 0000000000..9289cd75b1
--- /dev/null
+++ b/third_party/rlbox_wasm2c_sandbox/src/wasm2c_rt_minwasi.c
@@ -0,0 +1,799 @@
+/* A minimum wasi implementation supporting only stdin, stdout, stderr, argv
+ * (upto 1000 args), env (upto 1000 env), and clock functions. */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+// Macs priors to OSX 10.12 don't have the clock functions. So we will use mac
+// specific options
+# include <mach/mach_time.h>
+# include <sys/time.h>
+#endif
+
+#include "wasm-rt.h"
+#include "wasm2c_rt_minwasi.h"
+
+#ifndef WASM_RT_CORE_TYPES_DEFINED
+# define WASM_RT_CORE_TYPES_DEFINED
+typedef uint8_t u8;
+typedef int8_t s8;
+typedef uint16_t u16;
+typedef int16_t s16;
+typedef uint32_t u32;
+typedef int32_t s32;
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef float f32;
+typedef double f64;
+#endif
+
+#ifndef UNLIKELY
+# if defined(__GNUC__)
+# define UNLIKELY(x) __builtin_expect(!!(x), 0)
+# define LIKELY(x) __builtin_expect(!!(x), 1)
+# else
+# define UNLIKELY(x) (x)
+# define LIKELY(x) (x)
+# endif
+#endif
+
+#define TRAP(x) wasm_rt_trap(WASM_RT_TRAP_##x)
+
+#define WASI_MEMACCESS(mem, a) ((void*)&(mem->data[a]))
+
+#define WASI_MEMCHECK_SIZE(mem, a, sz) \
+ if (UNLIKELY(((u64)(a)) + sz > mem->size)) \
+ TRAP(OOB)
+
+#define WASI_CHECK_COPY(mem, a, sz, src) \
+ do { \
+ WASI_MEMCHECK_SIZE(mem, a, sz); \
+ memcpy(WASI_MEMACCESS(mem, a), src, sz); \
+ } while (0)
+
+#define WASI_MEMCHECK(mem, a, t) WASI_MEMCHECK_SIZE(mem, a, sizeof(t))
+
+#define DEFINE_WASI_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr) \
+ { \
+ WASI_MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ memcpy(&result, WASI_MEMACCESS(mem, addr), sizeof(t1)); \
+ return (t3)(t2)result; \
+ }
+
+#define DEFINE_WASI_STORE(name, t1, t2) \
+ static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) \
+ { \
+ WASI_MEMCHECK(mem, addr, t1); \
+ t1 wrapped = (t1)value; \
+ memcpy(WASI_MEMACCESS(mem, addr), &wrapped, sizeof(t1)); \
+ }
+
+DEFINE_WASI_LOAD(wasm_i32_load, u32, u32, u32);
+DEFINE_WASI_STORE(wasm_i32_store, u32, u32);
+DEFINE_WASI_STORE(wasm_i64_store, u64, u64);
+
+static bool safe_add_u32(u32* ret, u32 a, u32 b)
+{
+ if (UINT32_MAX - a < b) {
+ *ret = 0;
+ return false;
+ }
+ *ret = a + b;
+ return true;
+}
+
+// clang-format off
+
+////////////// Supported WASI APIs
+//
+// Clock operations
+// ----------------
+// errno_t clock_res_get(void* ctx, clockid_t clock_id, timestamp_t* resolution);
+// errno_t clock_time_get(void* ctx, clockid_t clock_id, timestamp_t precision, timestamp_t* time);
+//
+// File operations
+// ----------------
+// Only the default descriptors of STDIN, STDOUT, STDERR are allowed by the
+// runtime.
+//
+// errno_t fd_prestat_get(void* ctx, fd_t fd, prestat_t* buf);
+// errno_t fd_read(void* ctx, fd_t fd, const iovec_t* iovs, size_t iovs_len, size_t* nread);
+// errno_t fd_write(void* ctx, fd_t fd, const ciovec_t* iovs, size_t iovs_len, size_t* nwritten);
+
+////////////// Partially supported WASI APIs
+//
+// App environment operations
+// --------------------------
+// These APIs work but return an empty buffer
+//
+// errno_t args_get(void* ctx, char** argv, char* argv_buf);
+// errno_t args_sizes_get(void* ctx, size_t* argc, size_t* argv_buf_size);
+// errno_t environ_get(void* ctx, char** environment, char* environ_buf);
+// errno_t environ_sizes_get(void* ctx, size_t* environ_count, size_t* environ_buf_size);
+//
+// Proc exit operation
+// -------------------
+// This is a no-op here in this runtime as the focus is on library
+// sandboxing
+//
+// errno_t proc_exit(void* ctx, exitcode_t rval);
+
+////////////// Unsupported WASI APIs
+// errno_t fd_advise(void* ctx, fd_t fd, filesize_t offset, filesize_t len, advice_t advice);
+// errno_t fd_allocate(void* ctx, fd_t fd, filesize_t offset, filesize_t len);
+// errno_t fd_close(void* ctx, fd_t fd);
+// errno_t fd_datasync(void* ctx, fd_t fd);
+// errno_t fd_fdstat_get(void* ctx, fd_t fd, fdstat_t* buf);
+// errno_t fd_fdstat_set_flags(void* ctx, fd_t fd, fdflags_t flags);
+// errno_t fd_fdstat_set_rights(void* ctx, fd_t fd, rights_t fs_rights_base, rights_t fs_rights_inheriting);
+// errno_t fd_filestat_get(void* ctx, fd_t fd, filestat_t* buf);
+// errno_t fd_filestat_set_size(void* ctx, fd_t fd, filesize_t st_size);
+// errno_t fd_filestat_set_times(void* ctx, fd_t fd, timestamp_t st_atim, timestamp_t st_mtim, fstflags_t fst_flags);
+// errno_t fd_pread(void* ctx, fd_t fd, const iovec_t* iovs, size_t iovs_len, filesize_t offset, size_t* nread);
+// errno_t fd_prestat_dir_name(void* ctx, fd_t fd, char* path, size_t path_len);
+// errno_t fd_pwrite(void* ctx, fd_t fd, const ciovec_t* iovs, size_t iovs_len, filesize_t offset, size_t* nwritten);
+// errno_t fd_readdir(void* ctx, fd_t fd, void* buf, size_t buf_len, dircookie_t cookie, size_t* bufused);
+// errno_t fd_renumber(void* ctx, fd_t from, fd_t to);
+// errno_t fd_seek(void* ctx, fd_t fd, filedelta_t offset, whence_t whence, filesize_t* newoffset);
+// errno_t fd_sync(void* ctx, fd_t fd);
+// errno_t fd_tell(void* ctx, fd_t fd, filesize_t* offset);
+// errno_t path_create_directory(void* ctx, fd_t fd, const char* path, size_t path_len);
+// errno_t path_filestat_get(void* ctx, fd_t fd, lookupflags_t flags, const char* path, size_t path_len, filestat_t* buf);
+// errno_t path_filestat_set_times(void* ctx, fd_t fd, lookupflags_t flags, const char* path, size_t path_len, timestamp_t st_atim, timestamp_t st_mtim, fstflags_t fst_flags);
+// errno_t path_link(void* ctx, fd_t old_fd, lookupflags_t old_flags, const char* old_path, size_t old_path_len, fd_t new_fd, const char* new_path, size_t new_path_len);
+// errno_t path_open(void* ctx, fd_t dirfd, lookupflags_t dirflags, const char* path, size_t path_len, oflags_t o_flags, rights_t fs_rights_base, rights_t fs_rights_inheriting, fdflags_t fs_flags, fd_t* fd);
+// errno_t path_readlink(void* ctx, fd_t fd, const char* path, size_t path_len, char* buf, size_t buf_len, size_t* bufused);
+// errno_t path_remove_directory(void* ctx, fd_t fd, const char* path, size_t path_len);
+// errno_t path_rename(void* ctx, fd_t old_fd, const char* old_path, size_t old_path_len, fd_t new_fd, const char* new_path, size_t new_path_len);
+// errno_t path_symlink(void* ctx, const char* old_path, size_t old_path_len, fd_t fd, const char* new_path, size_t new_path_len);
+// errno_t path_unlink_file(void* ctx, fd_t fd, const char* path, size_t path_len);
+// errno_t poll_oneoff(void* ctx, const subscription_t* in, event_t* out, size_t nsubscriptions, size_t* nevents);
+// errno_t proc_raise(void* ctx, signal_t sig);
+// errno_t random_get(void* ctx, void* buf, size_t buf_len);
+// errno_t sched_yield(t* uvwasi);
+// errno_t sock_accept(void* ctx, fd_t sock, flags_t fdflags, fd* fd);
+// errno_t sock_recv(void* ctx, fd_t sock, const iovec_t* ri_data, size_t ri_data_len, riflags_t ri_flags, size_t* ro_datalen, roflags_t* ro_flags);
+// errno_t sock_send(void* ctx, fd_t sock, const ciovec_t* si_data, size_t si_data_len, siflags_t si_flags, size_t* so_datalen);
+// errno_t sock_shutdown(void* ctx, fd_t sock, sdflags_t how);
+
+// clang-format on
+
+// Success
+#define WASI_SUCCESS 0
+// Bad file descriptor.
+#define WASI_BADF_ERROR 8
+// Invalid argument
+#define WASI_INVAL_ERROR 28
+// Operation not permitted.
+#define WASI_PERM_ERROR 63
+// Syscall not implemented
+#define WASI_NOSYS_ERROR 53
+
+#define WASI_RET_ERR_ON_FAIL(exp) \
+ if (!(exp)) { \
+ return WASI_INVAL_ERROR; \
+ }
+
+/////////////////////////////////////////////////////////////
+// Clock operations
+/////////////////////////////////////////////////////////////
+
+#if defined(_WIN32)
+
+typedef struct
+{
+ LARGE_INTEGER counts_per_sec; /* conversion factor */
+} wasi_win_clock_info_t;
+
+static wasi_win_clock_info_t g_wasi_win_clock_info;
+static int g_os_data_initialized = 0;
+
+static bool os_clock_init()
+{
+ // From here:
+ // https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows/38212960#38212960
+ if (QueryPerformanceFrequency(&g_wasi_win_clock_info.counts_per_sec) == 0) {
+ return false;
+ }
+ g_os_data_initialized = 1;
+ return true;
+}
+
+static bool os_clock_init_instance(void** clock_data_pointer)
+{
+ if (!g_os_data_initialized) {
+ os_clock_init();
+ }
+
+ wasi_win_clock_info_t* alloc =
+ (wasi_win_clock_info_t*)malloc(sizeof(wasi_win_clock_info_t));
+ if (!alloc) {
+ return false;
+ }
+ memcpy(alloc, &g_wasi_win_clock_info, sizeof(wasi_win_clock_info_t));
+ *clock_data_pointer = alloc;
+ return true;
+}
+
+static void os_clock_cleanup_instance(void** clock_data_pointer)
+{
+ if (*clock_data_pointer == 0) {
+ free(*clock_data_pointer);
+ *clock_data_pointer = 0;
+ }
+}
+
+static int os_clock_gettime(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ wasi_win_clock_info_t* alloc = (wasi_win_clock_info_t*)clock_data;
+
+ LARGE_INTEGER count;
+ (void)clock_id;
+
+ if (alloc->counts_per_sec.QuadPart <= 0 ||
+ QueryPerformanceCounter(&count) == 0) {
+ return -1;
+ }
+
+# define BILLION 1000000000LL
+ out_struct->tv_sec = count.QuadPart / alloc->counts_per_sec.QuadPart;
+ out_struct->tv_nsec =
+ ((count.QuadPart % alloc->counts_per_sec.QuadPart) * BILLION) /
+ alloc->counts_per_sec.QuadPart;
+# undef BILLION
+
+ return 0;
+}
+
+static int os_clock_getres(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ (void)clock_id;
+ out_struct->tv_sec = 0;
+ out_struct->tv_nsec = 1000;
+ return 0;
+}
+
+#elif defined(__APPLE__) && defined(__MACH__)
+
+typedef struct
+{
+ mach_timebase_info_data_t timebase; /* numer = 0, denom = 0 */
+ struct timespec inittime; /* nanoseconds since 1-Jan-1970 to init() */
+ uint64_t initclock; /* ticks since boot to init() */
+} wasi_mac_clock_info_t;
+
+static wasi_mac_clock_info_t g_wasi_mac_clock_info;
+static int g_os_data_initialized = 0;
+
+static bool os_clock_init()
+{
+ // From here:
+ // https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x/21352348#21352348
+ if (mach_timebase_info(&g_wasi_mac_clock_info.timebase) != 0) {
+ return false;
+ }
+
+ // microseconds since 1 Jan 1970
+ struct timeval micro;
+ if (gettimeofday(&micro, NULL) != 0) {
+ return false;
+ }
+
+ g_wasi_mac_clock_info.initclock = mach_absolute_time();
+
+ g_wasi_mac_clock_info.inittime.tv_sec = micro.tv_sec;
+ g_wasi_mac_clock_info.inittime.tv_nsec = micro.tv_usec * 1000;
+
+ g_os_data_initialized = 1;
+ return true;
+}
+
+static bool os_clock_init_instance(void** clock_data_pointer)
+{
+ if (!g_os_data_initialized) {
+ os_clock_init();
+ }
+
+ wasi_mac_clock_info_t* alloc =
+ (wasi_mac_clock_info_t*)malloc(sizeof(wasi_mac_clock_info_t));
+ if (!alloc) {
+ return false;
+ }
+ memcpy(alloc, &g_wasi_mac_clock_info, sizeof(wasi_mac_clock_info_t));
+ *clock_data_pointer = alloc;
+ return true;
+}
+
+static void os_clock_cleanup_instance(void** clock_data_pointer)
+{
+ if (*clock_data_pointer == 0) {
+ free(*clock_data_pointer);
+ *clock_data_pointer = 0;
+ }
+}
+
+static int os_clock_gettime(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ int ret = 0;
+ wasi_mac_clock_info_t* alloc = (wasi_mac_clock_info_t*)clock_data;
+
+ // From here:
+ // https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x/21352348#21352348
+
+ (void)clock_id;
+ // ticks since init
+ uint64_t clock = mach_absolute_time() - alloc->initclock;
+ // nanoseconds since init
+ uint64_t nano = clock * (uint64_t)(alloc->timebase.numer) /
+ (uint64_t)(alloc->timebase.denom);
+ *out_struct = alloc->inittime;
+
+# define BILLION 1000000000L
+ out_struct->tv_sec += nano / BILLION;
+ out_struct->tv_nsec += nano % BILLION;
+ // normalize
+ out_struct->tv_sec += out_struct->tv_nsec / BILLION;
+ out_struct->tv_nsec = out_struct->tv_nsec % BILLION;
+# undef BILLION
+ return ret;
+}
+
+static int os_clock_getres(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ int ret = 0;
+ (void)clock_id;
+ out_struct->tv_sec = 0;
+ out_struct->tv_nsec = 1;
+ return ret;
+}
+
+#else
+
+static bool os_clock_init()
+{
+ return true;
+}
+
+static bool os_clock_init_instance(void** clock_data_pointer)
+{
+ (void)clock_data_pointer;
+ return true;
+}
+
+static void os_clock_cleanup_instance(void** clock_data_pointer)
+{
+ (void)clock_data_pointer;
+}
+
+static int os_clock_gettime(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ (void)clock_data;
+ int ret = clock_gettime(clock_id, out_struct);
+ return ret;
+}
+
+static int os_clock_getres(void* clock_data,
+ int clock_id,
+ struct timespec* out_struct)
+{
+ (void)clock_data;
+ int ret = clock_getres(clock_id, out_struct);
+ return ret;
+}
+
+#endif
+
+#define WASM_CLOCK_REALTIME 0
+#define WASM_CLOCK_MONOTONIC 1
+#define WASM_CLOCK_PROCESS_CPUTIME 2
+#define WASM_CLOCK_THREAD_CPUTIME_ID 3
+
+static int check_clock(u32 clock_id)
+{
+ return clock_id == WASM_CLOCK_REALTIME || clock_id == WASM_CLOCK_MONOTONIC ||
+ clock_id == WASM_CLOCK_PROCESS_CPUTIME ||
+ clock_id == WASM_CLOCK_THREAD_CPUTIME_ID;
+}
+
+// out is a pointer to a u64 timestamp in nanoseconds
+// https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-timestamp-u64
+u32 w2c_wasi__snapshot__preview1_clock_time_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 clock_id,
+ u64 precision,
+ u32 out)
+{
+ if (!check_clock(clock_id)) {
+ return WASI_INVAL_ERROR;
+ }
+
+ struct timespec out_struct;
+ int ret = os_clock_gettime(wasi_data->clock_data, clock_id, &out_struct);
+ u64 result =
+ ((u64)out_struct.tv_sec) * 1000 * 1000 * 1000 + ((u64)out_struct.tv_nsec);
+ wasm_i64_store(wasi_data->instance_memory, out, result);
+ return ret;
+}
+
+u32 w2c_wasi__snapshot__preview1_clock_res_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 clock_id,
+ u32 out)
+{
+ if (!check_clock(clock_id)) {
+ return WASI_INVAL_ERROR;
+ }
+
+ struct timespec out_struct;
+ int ret = os_clock_getres(wasi_data->clock_data, clock_id, &out_struct);
+ u64 result =
+ ((u64)out_struct.tv_sec) * 1000 * 1000 * 1000 + ((u64)out_struct.tv_nsec);
+ wasm_i64_store(wasi_data->instance_memory, out, result);
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////
+////////// File operations
+/////////////////////////////////////////////////////////////
+
+// Only allow stdin (0), stdout (1), stderr(2)
+
+#define WASM_STDIN 0
+#define WASM_STDOUT 1
+#define WASM_STDERR 2
+
+u32 w2c_wasi__snapshot__preview1_fd_prestat_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 fd,
+ u32 prestat)
+{
+ if (fd == WASM_STDIN || fd == WASM_STDOUT || fd == WASM_STDERR) {
+ return WASI_PERM_ERROR;
+ }
+ return WASI_BADF_ERROR;
+}
+
+u32 w2c_wasi__snapshot__preview1_fd_write(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 fd,
+ u32 iov,
+ u32 iovcnt,
+ u32 pnum)
+{
+ if (fd != WASM_STDOUT && fd != WASM_STDERR) {
+ return WASI_BADF_ERROR;
+ }
+
+ u32 num = 0;
+ for (u32 i = 0; i < iovcnt; i++) {
+ u32 ptr = wasm_i32_load(wasi_data->instance_memory, iov + i * 8);
+ u32 len = wasm_i32_load(wasi_data->instance_memory, iov + i * 8 + 4);
+
+ WASI_MEMCHECK_SIZE(wasi_data->instance_memory, ptr, len);
+
+ size_t result = fwrite(WASI_MEMACCESS(wasi_data->instance_memory, ptr),
+ 1 /* size */,
+ len /* n */,
+ fd == WASM_STDOUT ? stdout : stderr);
+
+ // Guaranteed by fwrite
+ assert(result <= len);
+
+ WASI_RET_ERR_ON_FAIL(safe_add_u32(&num, num, (u32)result));
+
+ if (((u32)result) != len) {
+ wasm_i32_store(wasi_data->instance_memory, pnum, num);
+ return WASI_PERM_ERROR;
+ }
+ }
+
+ wasm_i32_store(wasi_data->instance_memory, pnum, num);
+ return WASI_SUCCESS;
+}
+
+u32 w2c_wasi__snapshot__preview1_fd_read(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 fd,
+ u32 iov,
+ u32 iovcnt,
+ u32 pnum)
+{
+ if (fd != WASM_STDIN) {
+ return WASI_BADF_ERROR;
+ }
+
+ u32 num = 0;
+ for (u32 i = 0; i < iovcnt; i++) {
+ u32 ptr = wasm_i32_load(wasi_data->instance_memory, iov + i * 8);
+ u32 len = wasm_i32_load(wasi_data->instance_memory, iov + i * 8 + 4);
+
+ WASI_MEMCHECK_SIZE(wasi_data->instance_memory, ptr, len);
+ size_t result = fread(WASI_MEMACCESS(wasi_data->instance_memory, ptr),
+ 1 /* size */,
+ len /* n */,
+ stdin);
+
+ // Guaranteed by fwrite
+ assert(result <= len);
+
+ WASI_RET_ERR_ON_FAIL(safe_add_u32(&num, num, (u32)result));
+
+ if (((u32)result) != len) {
+ break; // nothing more to read
+ }
+ }
+ wasm_i32_store(wasi_data->instance_memory, pnum, num);
+ return WASI_SUCCESS;
+}
+
+/////////////////////////////////////////////////////////////
+// App environment operations
+/////////////////////////////////////////////////////////////
+
+#define ARGV_AND_ENV_LIMIT 1000
+
+static u32 strings_sizes_get(wasm_rt_memory_t* instance_memory,
+ const char* name,
+ u32 p_str_count,
+ u32 p_str_buff_size,
+ u32 string_count,
+ const char** strings)
+{
+ u32 chosen_count = string_count;
+ if (chosen_count > ARGV_AND_ENV_LIMIT) {
+ chosen_count = ARGV_AND_ENV_LIMIT;
+ printf("Truncated %s args to %d\n", name, ARGV_AND_ENV_LIMIT);
+ }
+
+ u32 curr_buf_size = 0;
+ for (u32 i = 0; i < chosen_count; i++) {
+ size_t original_len = strlen(strings[i]);
+ // len has to be at most u32 - 1
+ WASI_RET_ERR_ON_FAIL(original_len < (size_t)UINT32_MAX);
+
+ u32 len = (u32)original_len;
+ u32 len_plus_nullchar = len + 1;
+
+ WASI_RET_ERR_ON_FAIL(
+ safe_add_u32(&curr_buf_size, curr_buf_size, len_plus_nullchar));
+ }
+
+ wasm_i32_store(instance_memory, p_str_count, chosen_count);
+ wasm_i32_store(instance_memory, p_str_buff_size, curr_buf_size);
+ return WASI_SUCCESS;
+}
+
+static u32 strings_get(wasm_rt_memory_t* instance_memory,
+ const char* name,
+ u32 p_str_arr,
+ u32 p_str_buf,
+ u32 string_count,
+ const char** strings)
+{
+ u32 chosen_count = string_count;
+ if (chosen_count > ARGV_AND_ENV_LIMIT) {
+ chosen_count = ARGV_AND_ENV_LIMIT;
+ // Warning is already printed in get_size
+ }
+
+ u32 curr_buf_loc = 0;
+
+ for (u32 i = 0; i < chosen_count; i++) {
+ // Implement: p_str_arr[i] = p_str_buf[curr_buf_loc]
+ u32 target_argv_i_ref;
+ WASI_RET_ERR_ON_FAIL(safe_add_u32(&target_argv_i_ref, p_str_arr, i * 4));
+
+ u32 target_buf_curr_ref;
+ WASI_RET_ERR_ON_FAIL(
+ safe_add_u32(&target_buf_curr_ref, p_str_buf, curr_buf_loc));
+
+ wasm_i32_store(instance_memory, target_argv_i_ref, target_buf_curr_ref);
+
+ // Implement: strcpy(p_str_buf[curr_buf_loc], strings[i]);
+ size_t original_len = strlen(strings[i]);
+ // len has to be at most u32 - 1
+ WASI_RET_ERR_ON_FAIL(original_len < (size_t)UINT32_MAX);
+
+ u32 len = (u32)original_len;
+ u32 len_plus_nullchar = len + 1;
+
+ WASI_CHECK_COPY(
+ instance_memory, target_buf_curr_ref, len_plus_nullchar, strings[i]);
+ // Implement: curr_buf_loc += strlen(p_str_buf[curr_buf_loc])
+ WASI_RET_ERR_ON_FAIL(
+ safe_add_u32(&curr_buf_loc, curr_buf_loc, len_plus_nullchar));
+ }
+ return WASI_SUCCESS;
+}
+
+u32 w2c_wasi__snapshot__preview1_args_sizes_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 p_argc,
+ u32 p_argv_buf_size)
+{
+ return strings_sizes_get(wasi_data->instance_memory,
+ "main",
+ p_argc,
+ p_argv_buf_size,
+ wasi_data->main_argc,
+ wasi_data->main_argv);
+}
+
+u32 w2c_wasi__snapshot__preview1_args_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 p_argv,
+ u32 p_argv_buf)
+{
+ return strings_get(wasi_data->instance_memory,
+ "main",
+ p_argv,
+ p_argv_buf,
+ wasi_data->main_argc,
+ wasi_data->main_argv);
+}
+
+u32 w2c_wasi__snapshot__preview1_environ_sizes_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 p_env_count,
+ u32 p_env_buf_size)
+{
+ return strings_sizes_get(wasi_data->instance_memory,
+ "env",
+ p_env_count,
+ p_env_buf_size,
+ wasi_data->env_count,
+ wasi_data->env);
+}
+
+u32 w2c_wasi__snapshot__preview1_environ_get(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 p_env,
+ u32 p_env_buf)
+{
+ return strings_get(wasi_data->instance_memory,
+ "env",
+ p_env,
+ p_env_buf,
+ wasi_data->env_count,
+ wasi_data->env);
+}
+
+/////////////////////////////////////////////////////////////
+// Proc exit operation
+/////////////////////////////////////////////////////////////
+
+void w2c_wasi__snapshot__preview1_proc_exit(
+ w2c_wasi__snapshot__preview1* wasi_data,
+ u32 x)
+{
+#ifdef WASM2C_WASI_TRAP_ON_EXIT
+ TRAP(WASI);
+#else
+ exit(x);
+#endif
+}
+
+/////////////////////////////////////////////////////////////
+////////////// Unsupported WASI APIs
+/////////////////////////////////////////////////////////////
+
+#define STUB_IMPORT_IMPL(ret, name, params) \
+ ret name params { return WASI_NOSYS_ERROR; }
+
+// clang-format off
+
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_advise,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u64 b, u64 c, u32 d));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_allocate,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u64 b, u64 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_close,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 fd));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_datasync,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_fdstat_get,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_fdstat_set_flags,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_fdstat_set_rights,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u64 b, u64 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_filestat_get,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_filestat_set_size,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u64 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_filestat_set_times,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u64 b, u64 c, u32 d));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_pread,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u64 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_prestat_dir_name,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_pwrite,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u64 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_readdir,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u64 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_renumber,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_seek,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 fd, u64 offset, u32 whence, u32 new_offset));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_sync,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_fd_tell,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_create_directory,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_filestat_get,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_filestat_set_times,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u64 e, u64 f, u32 g));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_link,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e, u32 f, u32 g));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_open,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e, u64 f, u64 g, u32 h, u32 i));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_readlink,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e, u32 f));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_remove_directory,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_rename,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e, u32 f));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_symlink,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_path_unlink_file,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_poll_oneoff,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_proc_raise,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_random_get,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_sched_yield,
+ (w2c_wasi__snapshot__preview1* wasi_data));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_sock_accept,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_sock_recv,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e, u32 f));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_sock_send,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b, u32 c, u32 d, u32 e));
+STUB_IMPORT_IMPL(u32, w2c_wasi__snapshot__preview1_sock_shutdown,
+ (w2c_wasi__snapshot__preview1* wasi_data, u32 a, u32 b));
+
+// clang-format on
+
+/////////////////////////////////////////////////////////////
+////////// Misc
+/////////////////////////////////////////////////////////////
+
+bool minwasi_init()
+{
+ return os_clock_init();
+}
+
+bool minwasi_init_instance(w2c_wasi__snapshot__preview1* wasi_data)
+{
+ return os_clock_init_instance(&(wasi_data->clock_data));
+}
+
+void minwasi_cleanup_instance(w2c_wasi__snapshot__preview1* wasi_data)
+{
+ os_clock_cleanup_instance(&(wasi_data->clock_data));
+}