commit 6ccc62a9ce1e2feed3f6d085b011571cccfc8447
parent 9bd665b95d72f696db659675bf3c85367e1b8cf1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 3 Jun 2026 20:28:36 -0700
Implement wasm sandbox host shim
Diffstat:
15 files changed, 2457 insertions(+), 343 deletions(-)
diff --git a/driver/cmd/run.c b/driver/cmd/run.c
@@ -3,7 +3,6 @@
#include <kit/interp.h>
#include <kit/jit.h>
#include <kit/link.h>
-#include <kit/wasm.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -12,6 +11,7 @@
#include "driver.h"
#include "hosted.h"
#include "inputs.h"
+#include "wasm_run.h"
/* `kit run` — JIT-compile one or more inputs and invoke the entry symbol
* (default `main`) in-process. Args after `--` are passed to the JITed
@@ -19,10 +19,11 @@
* .o objects, .a archives) and target/diagnostic flag surface; libkit's
* JIT path forces PIC regardless of `-fPIC`/`-fPIE`/`-mcmodel`.
*
- * Host-symbol fallback (so JITed code can call libc) goes through
- * driver_dlsym_resolver in driver/env.c. The driver returns whatever the
- * entry returns, or 1 on a compile/link/lookup error. The entry is invoked
- * as `int(*)(int, char**)`. */
+ * Native host-symbol fallback (so JITed C/Toy/object code can call libc) goes
+ * through driver_dlsym_resolver in driver/env.c. Wasm source inputs do not get
+ * that fallback; host access is controlled by the Wasm import policy. The
+ * driver returns whatever the entry returns, or 1 on a compile/link/lookup
+ * error. The native entry is invoked as `int(*)(int, char**)`. */
#define RUN_TOOL "run"
@@ -46,6 +47,7 @@ typedef struct RunOptions {
DriverCflags cf;
DriverInputs inputs;
+ DriverWasmRunOptions wasm;
char** prog_argv; /* args after `--` */
uint32_t prog_argc;
@@ -260,9 +262,10 @@ void driver_help_run(void) {
" .a static archive\n"
" - read C source from stdin (single source only)\n"
"\n"
- " JITed code may call any host symbol resolvable via "
+ " Native JITed code may call any host symbol resolvable via "
"dlsym(RTLD_DEFAULT)\n"
- " — typically libc. The JIT path forces PIC; -fPIC / -fPIE / "
+ " — typically libc. Wasm source inputs do not use this fallback.\n"
+ " The JIT path forces PIC; -fPIC / -fPIE / "
"-mcmodel\n"
" are accepted but have no observable effect.\n"
"\n"
@@ -294,7 +297,45 @@ void driver_help_run(void) {
" -isystem DIR Add system-include search path\n"
" -D NAME[=BODY] Define a macro\n"
" -U NAME Undefine a builtin/predefined macro\n"
- "\n"
+ "\n")));
+ driver_printf(
+ "%.*s",
+ KIT_SLICE_ARG(KIT_SLICE_LIT(
+ "WASM SANDBOX\n"
+ " Wasm inputs run with a deny-by-default host-import policy. "
+ "No host\n"
+ " symbol or test-import fallback is enabled unless an explicit "
+ "flag\n"
+ " below requests it.\n"
+ " --wasm-memory-max=SIZE\n"
+ " Maximum total linear-memory reservation "
+ "(default 1G)\n"
+ " --wasm-instance-max=SIZE\n"
+ " Maximum runtime instance size (default 64M)\n"
+ " --wasm-memories-max=N\n"
+ " Maximum declared linear-memory count\n"
+ " --wasm-imports=deny|wasi|test\n"
+ " Import resolver policy (default deny). `test` "
+ "binds\n"
+ " only the in-tree env.host_add test shim.\n"
+ " `wasi` binds the configured WASI Preview1 "
+ "shim.\n"
+ " --wasm-wasi Alias for --wasm-imports=wasi\n"
+ " --wasm-env=none|allowlist|inherit\n"
+ " --wasm-env-pass=NAME, --wasm-env-set=NAME=VALUE\n"
+ " Guest environment policy for WASI env imports\n"
+ " --wasm-fs=none Filesystem disabled (default)\n"
+ " --wasm-map-dir=HOST=GUEST[:ro|rw]\n"
+ " --wasm-map-file=HOST=GUEST[:ro|rw]\n"
+ " --wasm-cwd=GUEST Guest cwd exposed through the WASI config\n"
+ " --wasm-stdio=null|inherit\n"
+ " --wasm-clock=none|monotonic|realtime\n"
+ " --wasm-random=none|host|seed:HEX\n"
+ " WASI host-resource policy; default is none\n"
+ "\n")));
+ driver_printf(
+ "%.*s",
+ KIT_SLICE_ARG(KIT_SLICE_LIT(
"ARGV PASSTHROUGH\n"
" -- End of `kit run` options. Tokens after `--` "
"are\n"
@@ -355,6 +396,7 @@ static int run_alloc_arrays(RunOptions* o, int argc) {
driver_errf(RUN_TOOL, "out of memory");
return 1;
}
+ if (driver_wasm_run_options_init(&o->wasm, o->env, bound) != 0) return 1;
return 0;
}
@@ -406,6 +448,25 @@ static int run_classify_positional(RunOptions* o, const char* a) {
return 0;
}
+static int run_path_is_wasm_source(const char* path) {
+ return driver_has_suffix(path, ".wat") || driver_has_suffix(path, ".wasm");
+}
+
+static int run_inputs_have_wasm_source(const DriverInputs* in) {
+ uint32_t i;
+ for (i = 0; i < in->nsources; ++i)
+ if (run_path_is_wasm_source(in->sources[i])) return 1;
+ return 0;
+}
+
+static int run_inputs_have_non_wasm(const DriverInputs* in) {
+ uint32_t i;
+ if (in->nsource_memory || in->nobject_files || in->narchives) return 1;
+ for (i = 0; i < in->nsources; ++i)
+ if (!run_path_is_wasm_source(in->sources[i])) return 1;
+ return 0;
+}
+
static int run_apply_hosted_profile(RunOptions* o) {
DriverHostedRequest req;
uint32_t i;
@@ -476,6 +537,12 @@ static int run_parse(int argc, char** argv, RunOptions* o) {
}
{
+ int wr = driver_wasm_run_try_consume(&o->wasm, RUN_TOOL, argc, argv, &i);
+ if (wr < 0) return 1;
+ if (wr > 0) continue;
+ }
+
+ {
int r =
driver_cflags_try_consume(&o->cf, o->env, RUN_TOOL, argc, argv, &i);
if (r < 0) return 1;
@@ -632,6 +699,12 @@ static int run_parse(int argc, char** argv, RunOptions* o) {
run_usage();
return 1;
}
+ if (run_inputs_have_wasm_source(&o->inputs) &&
+ run_inputs_have_non_wasm(&o->inputs)) {
+ driver_errf(RUN_TOOL,
+ "sandboxed wasm run does not support mixed native inputs");
+ return 1;
+ }
if (!o->entry) o->entry = "main";
if (run_apply_hosted_profile(o) != 0) return 1;
@@ -639,12 +712,15 @@ static int run_parse(int argc, char** argv, RunOptions* o) {
* the program name; under `kit run` there is no executable path, so
* use the first input's display name. */
o->prog_argv[0] = (char*)driver_inputs_first_name(&o->inputs);
+ o->wasm.args = (const char* const*)o->prog_argv;
+ o->wasm.nargs = o->prog_argc;
return 0;
}
static void run_options_release(RunOptions* o) {
size_t bound = o->argv_bound;
driver_hosted_plan_fini(o->env, &o->hosted);
+ driver_wasm_run_options_fini(&o->wasm);
driver_inputs_release(&o->inputs);
driver_target_features_fini(&o->target_features, o->env);
driver_cflags_fini(&o->cf, o->env);
@@ -671,309 +747,17 @@ static int run_compile_and_jit(RunOptions* o, KitCompiler* compiler,
const KitJitHost* host, KitJit** out_jit) {
KitCCompileOptions copts;
KitPreprocessOptions pp;
+ void* (*extern_resolver)(void*, KitSlice) = driver_dlsym_resolver;
run_fill_compile_opts(o, &copts);
driver_cflags_fill_pp(&o->cf, &pp);
+ if (run_inputs_have_wasm_source(&o->inputs) ||
+ driver_wasm_run_options_used(&o->wasm))
+ extern_resolver = NULL;
return driver_inputs_compile_and_jit(&o->inputs, compiler, host, &copts, &pp,
- o->entry, driver_dlsym_resolver, NULL,
- out_jit);
+ o->entry, extern_resolver, NULL, out_jit);
}
typedef int (*MainFn)(int, char**);
-typedef void (*WasmInitFn)(void*);
-typedef int (*WasmMainFn)(void*);
-
-/* Canned host import for the wasm-front test suite. Active when the
- * caller sets KIT_TEST_HOST_IMPORTS=1 — production runs ignore it.
- * Defined non-static so it has an external symbol the binder can call. */
-int32_t run_test_host_add(KitWasmInstance* inst, int32_t a, int32_t b) {
- (void)inst;
- return a + b;
-}
-
-typedef struct RunWasmMemoryPrefix {
- uint8_t* data;
- uint64_t pages;
- uint64_t max_pages;
- uint32_t flags;
-} RunWasmMemoryPrefix;
-
-typedef struct RunWasmInstanceAlloc {
- uint8_t* instance;
- uint8_t** memories;
- uint64_t* memory_bytes;
- uint32_t nmemories;
- uint64_t instance_bytes;
-} RunWasmInstanceAlloc;
-
-#define RUN_WASM_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
-#define RUN_WASM_MAX_TOTAL_MEMORY_BYTES (1024ull * 1024ull * 1024ull)
-
-static int run_wasm_u64_to_size(RunOptions* ro, uint64_t n, size_t* out,
- const char* what) {
- (void)ro;
- if (n > (uint64_t)SIZE_MAX) {
- driver_errf(RUN_TOOL, "wasm %s exceeds host size_t", what);
- return 1;
- }
- *out = (size_t)n;
- return 0;
-}
-
-static int run_wasm_page_bytes(RunOptions* ro, uint64_t pages, uint64_t* out) {
- (void)ro;
- if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
- driver_errf(RUN_TOOL, "wasm memory size overflows");
- return 1;
- }
- *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
- return 0;
-}
-
-static int run_wasm_host_add_type(const KitWasmImportType* type) {
- return type && type->nparams == 2u && type->nresults == 1u &&
- type->params[0] == KIT_WASM_VAL_I32 &&
- type->params[1] == KIT_WASM_VAL_I32 &&
- type->results[0] == KIT_WASM_VAL_I32;
-}
-
-static void* run_wasm_test_resolve(void* user, const char* module,
- const char* field,
- const KitWasmImportType* type) {
- extern int32_t run_test_host_add(KitWasmInstance*, int32_t, int32_t);
- (void)user;
- if (!module || !field) return NULL;
- if (driver_streq(module, "env") && driver_streq(field, "host_add") &&
- run_wasm_host_add_type(type))
- return (void*)(uintptr_t)run_test_host_add;
- return NULL;
-}
-
-static void run_wasm_free_instance(RunOptions* ro,
- RunWasmInstanceAlloc* alloc);
-
-/* Allocate and wire a Wasm instance + linear memory and bind host imports.
- * Returns 0 on success (caller owns the returned instance and memory),
- * nonzero on error (already freed). Shared by the JIT and interpreter paths. */
-static int run_wasm_make_instance(RunOptions* ro, KitCompiler* compiler,
- KitJit* jit,
- RunWasmInstanceAlloc* alloc_out) {
- KitWasmRuntimeLayout layout;
- RunWasmInstanceAlloc alloc;
- size_t instance_size;
- const KitWasmHostImport* h_imports = NULL;
- size_t h_nimports = 0;
- KitWasmResolveFn h_resolve = NULL;
- void* h_user = NULL;
- uint64_t total_memory_bytes = 0;
- KitStatus layout_st;
- memset(&alloc, 0, sizeof alloc);
- if (!alloc_out) return 1;
- memset(alloc_out, 0, sizeof *alloc_out);
- layout_st = kit_wasm_get_runtime_layout(jit, &layout);
- if (layout_st != KIT_OK) {
- driver_errf(RUN_TOOL, layout_st == KIT_NOT_FOUND
- ? "wasm runtime layout metadata missing"
- : "wasm runtime layout metadata malformed");
- return 1;
- }
- alloc.instance_bytes = layout.instance_size ? layout.instance_size : 1u;
- if (alloc.instance_bytes > RUN_WASM_MAX_INSTANCE_BYTES) {
- driver_errf(RUN_TOOL, "wasm instance too large: %llu bytes",
- (unsigned long long)alloc.instance_bytes);
- return 1;
- }
- if (run_wasm_u64_to_size(ro, alloc.instance_bytes, &instance_size,
- "instance") != 0)
- return 1;
- alloc.instance = (uint8_t*)driver_alloc_zeroed(ro->env, instance_size);
- alloc.nmemories = layout.nmemories;
- if (!alloc.instance) {
- driver_errf(RUN_TOOL, "out of memory");
- return 1;
- }
- if (layout.nmemories) {
- size_t ptr_bytes;
- size_t size_bytes;
- if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*) ||
- (uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint64_t)) {
- driver_errf(RUN_TOOL, "wasm memory count too large");
- driver_free(ro->env, alloc.instance, instance_size);
- return 1;
- }
- ptr_bytes = (size_t)layout.nmemories * sizeof(uint8_t*);
- size_bytes = (size_t)layout.nmemories * sizeof(uint64_t);
- alloc.memories = (uint8_t**)driver_alloc_zeroed(ro->env, ptr_bytes);
- alloc.memory_bytes = (uint64_t*)driver_alloc_zeroed(ro->env, size_bytes);
- if (!alloc.memories || !alloc.memory_bytes) {
- driver_errf(RUN_TOOL, "out of memory");
- driver_free(ro->env, alloc.memory_bytes, size_bytes);
- driver_free(ro->env, alloc.memories, ptr_bytes);
- driver_free(ro->env, alloc.instance, instance_size);
- return 1;
- }
- }
- for (uint32_t i = 0; i < layout.nmemories; ++i) {
- const KitWasmMemoryLayout* ml = &layout.memories[i];
- RunWasmMemoryPrefix* rec;
- uint64_t mem_bytes;
- size_t mem_size;
- if (ml->max_pages < ml->min_pages) {
- driver_errf(RUN_TOOL, "wasm memory maximum below minimum");
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- if (ml->offset > alloc.instance_bytes ||
- alloc.instance_bytes - ml->offset < sizeof(RunWasmMemoryPrefix)) {
- driver_errf(RUN_TOOL, "wasm memory record outside instance");
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- if (run_wasm_page_bytes(ro, ml->max_pages, &mem_bytes) != 0) {
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- if (mem_bytes > RUN_WASM_MAX_TOTAL_MEMORY_BYTES ||
- total_memory_bytes > RUN_WASM_MAX_TOTAL_MEMORY_BYTES - mem_bytes) {
- driver_errf(RUN_TOOL, "wasm linear memory reservation exceeds %llu bytes",
- (unsigned long long)RUN_WASM_MAX_TOTAL_MEMORY_BYTES);
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- total_memory_bytes += mem_bytes;
- if (run_wasm_u64_to_size(ro, mem_bytes, &mem_size,
- "linear memory") != 0) {
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- if (mem_size) {
- alloc.memories[i] = (uint8_t*)driver_alloc_zeroed(ro->env, mem_size);
- if (!alloc.memories[i]) {
- driver_errf(RUN_TOOL, "out of memory");
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- }
- alloc.memory_bytes[i] = mem_bytes;
- rec = (RunWasmMemoryPrefix*)(alloc.instance + ml->offset);
- rec->data = alloc.memories[i];
- }
- kit_wasm_get_host_imports(compiler, &h_imports, &h_nimports, &h_resolve,
- &h_user);
- if (h_nimports == 0 && !h_resolve && driver_getenv("KIT_TEST_HOST_IMPORTS")) {
- h_resolve = run_wasm_test_resolve;
- h_user = NULL;
- }
- {
- KitStatus bind_st = kit_wasm_bind_host_imports(
- compiler, jit, (KitWasmInstance*)alloc.instance, h_imports, h_nimports,
- h_resolve, h_user);
- if (bind_st != KIT_OK) {
- const char* msg = bind_st == KIT_NOT_FOUND
- ? "wasm host import unresolved"
- : (bind_st == KIT_UNSUPPORTED
- ? "wasm host import kind unsupported"
- : "wasm host import metadata malformed");
- driver_errf(RUN_TOOL, "%s", msg);
- run_wasm_free_instance(ro, &alloc);
- return 1;
- }
- }
- *alloc_out = alloc;
- return 0;
-}
-
-static void run_wasm_free_instance(RunOptions* ro,
- RunWasmInstanceAlloc* alloc) {
- if (!alloc) return;
- if (alloc->memories) {
- for (uint32_t i = 0; i < alloc->nmemories; ++i) {
- size_t mem_size = 0;
- if (alloc->memories[i] &&
- run_wasm_u64_to_size(ro, alloc->memory_bytes[i], &mem_size,
- "linear memory") == 0)
- driver_free(ro->env, alloc->memories[i], mem_size);
- }
- driver_free(ro->env, alloc->memories,
- (size_t)alloc->nmemories * sizeof(uint8_t*));
- }
- if (alloc->memory_bytes)
- driver_free(ro->env, alloc->memory_bytes,
- (size_t)alloc->nmemories * sizeof(uint64_t));
- if (alloc->instance) {
- size_t instance_size = 0;
- if (run_wasm_u64_to_size(ro, alloc->instance_bytes, &instance_size,
- "instance") == 0)
- driver_free(ro->env, alloc->instance, instance_size);
- }
- memset(alloc, 0, sizeof *alloc);
-}
-
-static int run_call_wasm_entry(RunOptions* ro, KitCompiler* compiler,
- KitJit* jit, void* entry, int* rc_out) {
- void* init_sym = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init"));
- RunWasmInstanceAlloc alloc;
- union {
- void* p;
- WasmInitFn fn;
- } init_u;
- union {
- void* p;
- WasmMainFn fn;
- } entry_u;
- if (!init_sym) return 0;
- if (run_wasm_make_instance(ro, compiler, jit, &alloc) != 0) {
- *rc_out = 1;
- return 1;
- }
- init_u.p = init_sym;
- entry_u.p = entry;
- init_u.fn((KitWasmInstance*)alloc.instance);
- *rc_out = entry_u.fn(alloc.instance);
- run_wasm_free_instance(ro, &alloc);
- return 1;
-}
-
-/* Wasm entry through the interpreter (--no-jit). Sets up the instance like the
- * JIT path, then runs __kit_wasm_init and the entry as InterpFuncs, passing
- * the instance pointer as the (single) argument. Returns 0 if the module is
- * not a Wasm module (no interpretable __kit_wasm_init); otherwise 1 with
- * *rc_out set. */
-static int run_call_wasm_entry_interp(RunOptions* ro, KitCompiler* compiler,
- KitJit* jit, KitInterpProgram* interp,
- int* rc_out) {
- KitInterpFunc* init_fn =
- kit_interp_lookup(interp, KIT_SLICE_LIT("__kit_wasm_init"));
- KitInterpFunc* entry_fn =
- kit_interp_lookup(interp, kit_slice_cstr(ro->entry));
- RunWasmInstanceAlloc alloc;
- uint64_t args[1];
- int64_t ret = 0;
- KitInterpStatus s;
- if (!init_fn) return 0; /* not a Wasm module */
- if (!entry_fn) {
- driver_errf(RUN_TOOL, "interp: wasm entry %.*s has no interpretable IR",
- KIT_SLICE_ARG(kit_slice_cstr(ro->entry)));
- *rc_out = 1;
- return 1;
- }
- if (run_wasm_make_instance(ro, compiler, jit, &alloc) != 0) {
- *rc_out = 1;
- return 1;
- }
- args[0] = (uint64_t)(uintptr_t)alloc.instance;
- s = kit_interp_call_args(interp, init_fn, args, 1u, &ret);
- if (s == KIT_INTERP_DONE)
- s = kit_interp_call_args(interp, entry_fn, args, 1u, &ret);
- if (s == KIT_INTERP_DONE) {
- *rc_out = (int)ret;
- } else {
- driver_errf(RUN_TOOL, "interp: could not execute wasm entry %.*s",
- KIT_SLICE_ARG(kit_slice_cstr(ro->entry)));
- *rc_out = 1;
- }
- run_wasm_free_instance(ro, &alloc);
- return 1;
-}
/* Host-identity symbol resolver for the interpreter.
*
@@ -1179,8 +963,14 @@ int driver_run(int argc, char** argv) {
kit_interp_program_set_host(interp, &host);
/* Wasm modules need their instance/linear-memory set up and a 2-call
* (init, entry) sequence with the instance pointer as the argument. */
- if (run_call_wasm_entry_interp(&ro, compiler, jit, interp, &rc))
+ if (driver_wasm_run_call_entry_interp(&ro.wasm, RUN_TOOL, compiler, jit,
+ interp, ro.entry, &rc))
+ goto after_entry;
+ if (driver_wasm_run_options_used(&ro.wasm)) {
+ driver_errf(RUN_TOOL, "wasm sandbox flags require a wasm input");
+ rc = 1;
goto after_entry;
+ }
ifn = kit_interp_lookup(interp, kit_slice_cstr(ro.entry));
if (!ifn) {
driver_errf(RUN_TOOL,
@@ -1210,8 +1000,15 @@ int driver_run(int argc, char** argv) {
run_metrics_begin(metrics, "run.entry_call");
if (ro.bench_time) bench_exec_start = driver_now_ns();
- if (!run_call_wasm_entry(&ro, compiler, jit, sym, &rc))
- rc = entry_fn((int)ro.prog_argc, ro.prog_argv);
+ if (!driver_wasm_run_call_entry(&ro.wasm, RUN_TOOL, compiler, jit, sym,
+ &rc)) {
+ if (driver_wasm_run_options_used(&ro.wasm)) {
+ driver_errf(RUN_TOOL, "wasm sandbox flags require a wasm input");
+ rc = 1;
+ } else {
+ rc = entry_fn((int)ro.prog_argc, ro.prog_argv);
+ }
+ }
if (ro.bench_time) bench_exec_end = driver_now_ns();
run_metrics_end(metrics, "run.entry_call");
after_entry:
diff --git a/driver/env.h b/driver/env.h
@@ -155,6 +155,10 @@ void driver_memcpy(void* dst, const void* src, size_t n);
* not close stdout. */
KitWriter* driver_stdout_writer(DriverEnv*);
+/* Opens a Writer that writes to stderr. close frees the struct but does
+ * not close stderr. */
+KitWriter* driver_stderr_writer(DriverEnv*);
+
/* Test whether `path` names an existing filesystem entry (any type).
* Returns nonzero on existence, zero otherwise. Used by library-path
* resolution; intentionally distinct from read_all so candidate-search
@@ -233,6 +237,10 @@ int driver_random_bytes(uint8_t* out, size_t n);
* putenv from any caller. */
const char* driver_getenv(const char* name);
+/* Borrow the process environment as NAME=VALUE strings. The array and strings
+ * are libc-owned and remain valid until the process environment is mutated. */
+const char* const* driver_environ(void);
+
/* Read all of stdin into a freshly-allocated buffer. On success returns 1
* and stores the buffer/size in out_data/out_size; the caller frees via
* driver_free(env, *out_data, *out_size). Returns 0 on read failure or
diff --git a/driver/env/posix.c b/driver/env/posix.c
@@ -22,6 +22,8 @@
#include "env_posix.h"
+extern char** environ;
+
/* ---------------- exec memory: single-mapping core + registry ----------------
*/
@@ -249,11 +251,11 @@ static KitStatus stdio_w_status(KitWriter* w) {
}
static void stdio_w_close(KitWriter* w) {
DriverStdioWriter* sw = (DriverStdioWriter*)w;
- fflush(sw->fp); /* flush but do not close stdout */
+ fflush(sw->fp); /* flush but do not close the borrowed stdio stream */
sw->heap->free(sw->heap, sw, sizeof(*sw));
}
-KitWriter* driver_stdout_writer(DriverEnv* e) {
+static KitWriter* driver_stdio_writer(DriverEnv* e, FILE* fp) {
DriverStdioWriter* sw = (DriverStdioWriter*)e->heap->alloc(
e->heap, sizeof(*sw), _Alignof(DriverStdioWriter));
if (!sw) return NULL;
@@ -263,11 +265,23 @@ KitWriter* driver_stdout_writer(DriverEnv* e) {
sw->base.status = stdio_w_status;
sw->base.close = stdio_w_close;
sw->heap = e->heap;
- sw->fp = stdout;
+ sw->fp = fp;
sw->status = KIT_OK;
return &sw->base;
}
+KitWriter* driver_stdout_writer(DriverEnv* e) {
+ return driver_stdio_writer(e, stdout);
+}
+
+KitWriter* driver_stderr_writer(DriverEnv* e) {
+ return driver_stdio_writer(e, stderr);
+}
+
+const char* const* driver_environ(void) {
+ return (const char* const*)environ;
+}
+
/* ---------------- file_io (POSIX open/read/write/stat) ---------------- */
static KitStatus posix_read_all(void* user, const char* path,
diff --git a/driver/env/windows.c b/driver/env/windows.c
@@ -54,6 +54,8 @@
#include "env_internal.h"
+extern char** _environ;
+
/* Win32 dbg interrupt code: a synthetic signo handed up to on_fault. The
* value just needs to be distinct from real exception codes; we pick a
* small positive int so it round-trips through the int field of
@@ -392,7 +394,7 @@ static void stdio_w_close(KitWriter* w) {
sw->heap->free(sw->heap, sw, sizeof(*sw));
}
-KitWriter* driver_stdout_writer(DriverEnv* e) {
+static KitWriter* driver_stdio_writer(DriverEnv* e, FILE* fp) {
DriverStdioWriter* sw = (DriverStdioWriter*)e->heap->alloc(
e->heap, sizeof(*sw), _Alignof(DriverStdioWriter));
if (!sw) return NULL;
@@ -402,11 +404,23 @@ KitWriter* driver_stdout_writer(DriverEnv* e) {
sw->base.status = stdio_w_status;
sw->base.close = stdio_w_close;
sw->heap = e->heap;
- sw->fp = stdout;
+ sw->fp = fp;
sw->status = KIT_OK;
return &sw->base;
}
+KitWriter* driver_stdout_writer(DriverEnv* e) {
+ return driver_stdio_writer(e, stdout);
+}
+
+KitWriter* driver_stderr_writer(DriverEnv* e) {
+ return driver_stdio_writer(e, stderr);
+}
+
+const char* const* driver_environ(void) {
+ return (const char* const*)_environ;
+}
+
/* ============================================================
* file_io (CreateFileW + ReadFile/WriteFile)
* ============================================================ */
diff --git a/driver/lib/wasm_run.c b/driver/lib/wasm_run.c
@@ -0,0 +1,962 @@
+#include "wasm_run.h"
+
+#include <kit/core.h>
+#include <kit/wasm.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "driver.h"
+
+#define DRIVER_WASM_DEFAULT_MAX_INSTANCE_BYTES (64ull * 1024ull * 1024ull)
+#define DRIVER_WASM_DEFAULT_MAX_TOTAL_MEMORY_BYTES \
+ (1024ull * 1024ull * 1024ull)
+
+typedef void (*DriverWasmInitFn)(KitWasmInstance*);
+typedef int (*DriverWasmMainFn)(KitWasmInstance*);
+
+typedef struct DriverWasmOwnedString {
+ char* ptr;
+ size_t size;
+} DriverWasmOwnedString;
+
+typedef struct DriverWasmHostBuild {
+ DriverEnv* env;
+ const DriverWasmRunOptions* opts;
+ const char* tool;
+
+ KitWasmHost* host;
+ const char** env_entries;
+ uint32_t nenv_entries;
+ uint32_t env_cap;
+ KitWasmFsMount* mounts;
+ uint32_t nmounts;
+ uint32_t mounts_cap;
+ DriverWasmOwnedString* owned;
+ uint32_t nowned;
+ uint32_t owned_cap;
+ uint64_t random_state;
+} DriverWasmHostBuild;
+
+static int wasm_list_push(const char* tool, const char** items, size_t cap,
+ uint32_t* count, const char* value) {
+ if (*count >= cap || *count == UINT32_MAX) {
+ driver_errf(tool, "too many wasm sandbox options");
+ return 1;
+ }
+ items[*count] = value;
+ *count += 1u;
+ return 0;
+}
+
+int driver_wasm_run_options_init(DriverWasmRunOptions* o, DriverEnv* env,
+ size_t argv_bound) {
+ size_t bytes = argv_bound * sizeof(const char*);
+ memset(o, 0, sizeof *o);
+ o->env = env;
+ o->argv_bound = argv_bound;
+ o->max_instance_bytes = DRIVER_WASM_DEFAULT_MAX_INSTANCE_BYTES;
+ o->max_total_memory_bytes = DRIVER_WASM_DEFAULT_MAX_TOTAL_MEMORY_BYTES;
+ o->max_memories = UINT32_MAX;
+ o->imports = DRIVER_WASM_IMPORT_DENY;
+ o->env_mode = DRIVER_WASM_ENV_NONE;
+ o->stdio_mode = DRIVER_WASM_STDIO_NULL;
+ o->clock_mode = DRIVER_WASM_CLOCK_NONE;
+ o->random_mode = DRIVER_WASM_RANDOM_NONE;
+ if (argv_bound == 0) return 0;
+ o->env_pass = (const char**)driver_alloc_zeroed(env, bytes);
+ o->env_set = (const char**)driver_alloc_zeroed(env, bytes);
+ o->map_dirs = (const char**)driver_alloc_zeroed(env, bytes);
+ o->map_files = (const char**)driver_alloc_zeroed(env, bytes);
+ if (!o->env_pass || !o->env_set || !o->map_dirs || !o->map_files) {
+ driver_errf("run", "out of memory");
+ return 1;
+ }
+ return 0;
+}
+
+void driver_wasm_run_options_fini(DriverWasmRunOptions* o) {
+ size_t bytes;
+ if (!o) return;
+ bytes = o->argv_bound * sizeof(const char*);
+ if (o->env_pass) driver_free(o->env, o->env_pass, bytes);
+ if (o->env_set) driver_free(o->env, o->env_set, bytes);
+ if (o->map_dirs) driver_free(o->env, o->map_dirs, bytes);
+ if (o->map_files) driver_free(o->env, o->map_files, bytes);
+ memset(o, 0, sizeof *o);
+}
+
+static int wasm_parse_u64_dec(const char* s, uint64_t* out) {
+ uint64_t v = 0;
+ int any = 0;
+ if (!s) return 1;
+ while (*s >= '0' && *s <= '9') {
+ unsigned d = (unsigned)(*s - '0');
+ if (v > (UINT64_MAX - d) / 10u) return 1;
+ v = v * 10u + d;
+ any = 1;
+ s++;
+ }
+ if (!any || *s) return 1;
+ *out = v;
+ return 0;
+}
+
+static int wasm_parse_size(const char* s, uint64_t* out) {
+ uint64_t v = 0;
+ uint64_t mul = 1;
+ int any = 0;
+ if (!s) return 1;
+ while (*s >= '0' && *s <= '9') {
+ unsigned d = (unsigned)(*s - '0');
+ if (v > (UINT64_MAX - d) / 10u) return 1;
+ v = v * 10u + d;
+ any = 1;
+ s++;
+ }
+ if (!any) return 1;
+ if (*s) {
+ char c = *s++;
+ if (c >= 'a' && c <= 'z') c = (char)(c - ('a' - 'A'));
+ if (c == 'K')
+ mul = 1024ull;
+ else if (c == 'M')
+ mul = 1024ull * 1024ull;
+ else if (c == 'G')
+ mul = 1024ull * 1024ull * 1024ull;
+ else
+ return 1;
+ if (*s == 'i' || *s == 'I') s++;
+ if (*s == 'b' || *s == 'B') s++;
+ if (*s) return 1;
+ }
+ if (v > UINT64_MAX / mul) return 1;
+ *out = v * mul;
+ return 0;
+}
+
+static const char* wasm_take_value(const char* tool, int argc, char** argv,
+ int* index, const char* flag,
+ const char* inline_value) {
+ if (inline_value) return inline_value;
+ if (*index + 1 >= argc) {
+ driver_errf(tool, "%.*s requires an argument",
+ KIT_SLICE_ARG(kit_slice_cstr(flag)));
+ return NULL;
+ }
+ *index += 1;
+ return argv[*index];
+}
+
+static int wasm_set_import_mode(const char* tool, DriverWasmRunOptions* o,
+ const char* value) {
+ if (driver_streq(value, "deny") || driver_streq(value, "none")) {
+ o->imports = DRIVER_WASM_IMPORT_DENY;
+ return 0;
+ }
+ if (driver_streq(value, "wasi")) {
+ o->imports = DRIVER_WASM_IMPORT_WASI;
+ return 0;
+ }
+ if (driver_streq(value, "test")) {
+ o->imports = DRIVER_WASM_IMPORT_TEST;
+ return 0;
+ }
+ driver_errf(tool, "unknown --wasm-imports value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(value)));
+ return 1;
+}
+
+static int wasm_set_env_mode(const char* tool, DriverWasmRunOptions* o,
+ const char* value) {
+ if (driver_streq(value, "none")) {
+ o->env_mode = DRIVER_WASM_ENV_NONE;
+ return 0;
+ }
+ if (driver_streq(value, "allowlist")) {
+ o->env_mode = DRIVER_WASM_ENV_ALLOWLIST;
+ return 0;
+ }
+ if (driver_streq(value, "inherit")) {
+ o->env_mode = DRIVER_WASM_ENV_INHERIT;
+ return 0;
+ }
+ driver_errf(tool, "unknown --wasm-env value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(value)));
+ return 1;
+}
+
+static int wasm_set_stdio_mode(const char* tool, DriverWasmRunOptions* o,
+ const char* value) {
+ if (driver_streq(value, "null")) {
+ o->stdio_mode = DRIVER_WASM_STDIO_NULL;
+ return 0;
+ }
+ if (driver_streq(value, "inherit")) {
+ o->stdio_mode = DRIVER_WASM_STDIO_INHERIT;
+ return 0;
+ }
+ driver_errf(tool, "unknown --wasm-stdio value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(value)));
+ return 1;
+}
+
+static int wasm_set_clock_mode(const char* tool, DriverWasmRunOptions* o,
+ const char* value) {
+ if (driver_streq(value, "none")) {
+ o->clock_mode = DRIVER_WASM_CLOCK_NONE;
+ return 0;
+ }
+ if (driver_streq(value, "monotonic")) {
+ o->clock_mode = DRIVER_WASM_CLOCK_MONOTONIC;
+ return 0;
+ }
+ if (driver_streq(value, "realtime")) {
+ o->clock_mode = DRIVER_WASM_CLOCK_REALTIME;
+ return 0;
+ }
+ driver_errf(tool, "unknown --wasm-clock value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(value)));
+ return 1;
+}
+
+static int wasm_set_random_mode(const char* tool, DriverWasmRunOptions* o,
+ const char* value) {
+ if (driver_streq(value, "none")) {
+ o->random_mode = DRIVER_WASM_RANDOM_NONE;
+ o->random_seed = NULL;
+ return 0;
+ }
+ if (driver_streq(value, "host")) {
+ o->random_mode = DRIVER_WASM_RANDOM_HOST;
+ o->random_seed = NULL;
+ return 0;
+ }
+ if (driver_strneq(value, "seed:", 5)) {
+ o->random_mode = DRIVER_WASM_RANDOM_SEED;
+ o->random_seed = value + 5;
+ return 0;
+ }
+ driver_errf(tool, "unknown --wasm-random value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(value)));
+ return 1;
+}
+
+int driver_wasm_run_try_consume(DriverWasmRunOptions* o, const char* tool,
+ int argc, char** argv, int* index) {
+ const char* a = argv[*index];
+ const char* v;
+ uint64_t u64;
+ if (!driver_strneq(a, "--wasm-", 7)) return 0;
+ o->used = 1;
+
+ if (driver_streq(a, "--wasm-wasi")) {
+ o->imports = DRIVER_WASM_IMPORT_WASI;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-memory-max") ||
+ driver_strneq(a, "--wasm-memory-max=", 18)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-memory-max",
+ driver_strneq(a, "--wasm-memory-max=", 18) ? a + 18
+ : NULL);
+ if (!v) return -1;
+ if (wasm_parse_size(v, &u64) != 0) {
+ driver_errf(tool, "--wasm-memory-max requires a byte size");
+ return -1;
+ }
+ o->max_total_memory_bytes = u64;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-instance-max") ||
+ driver_strneq(a, "--wasm-instance-max=", 20)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-instance-max",
+ driver_strneq(a, "--wasm-instance-max=", 20) ? a + 20
+ : NULL);
+ if (!v) return -1;
+ if (wasm_parse_size(v, &u64) != 0) {
+ driver_errf(tool, "--wasm-instance-max requires a byte size");
+ return -1;
+ }
+ o->max_instance_bytes = u64;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-memories-max") ||
+ driver_strneq(a, "--wasm-memories-max=", 20)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-memories-max",
+ driver_strneq(a, "--wasm-memories-max=", 20) ? a + 20
+ : NULL);
+ if (!v) return -1;
+ if (wasm_parse_u64_dec(v, &u64) != 0 || u64 > UINT32_MAX) {
+ driver_errf(tool, "--wasm-memories-max requires a non-negative integer");
+ return -1;
+ }
+ o->max_memories = (uint32_t)u64;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-imports") ||
+ driver_strneq(a, "--wasm-imports=", 15)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-imports",
+ driver_strneq(a, "--wasm-imports=", 15) ? a + 15
+ : NULL);
+ if (!v) return -1;
+ return wasm_set_import_mode(tool, o, v) == 0 ? 1 : -1;
+ }
+ if (driver_streq(a, "--wasm-env") || driver_strneq(a, "--wasm-env=", 11)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-env",
+ driver_strneq(a, "--wasm-env=", 11) ? a + 11 : NULL);
+ if (!v) return -1;
+ return wasm_set_env_mode(tool, o, v) == 0 ? 1 : -1;
+ }
+ if (driver_streq(a, "--wasm-env-pass") ||
+ driver_strneq(a, "--wasm-env-pass=", 16)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-env-pass",
+ driver_strneq(a, "--wasm-env-pass=", 16) ? a + 16
+ : NULL);
+ if (!v) return -1;
+ if (wasm_list_push(tool, o->env_pass, o->argv_bound, &o->nenv_pass, v) !=
+ 0)
+ return -1;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-env-set") ||
+ driver_strneq(a, "--wasm-env-set=", 15)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-env-set",
+ driver_strneq(a, "--wasm-env-set=", 15) ? a + 15
+ : NULL);
+ if (!v) return -1;
+ if (wasm_list_push(tool, o->env_set, o->argv_bound, &o->nenv_set, v) != 0)
+ return -1;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-fs") || driver_strneq(a, "--wasm-fs=", 10)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-fs",
+ driver_strneq(a, "--wasm-fs=", 10) ? a + 10 : NULL);
+ if (!v) return -1;
+ if (!driver_streq(v, "none")) {
+ driver_errf(tool, "unknown --wasm-fs value: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(v)));
+ return -1;
+ }
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-map-dir") ||
+ driver_strneq(a, "--wasm-map-dir=", 15)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-map-dir",
+ driver_strneq(a, "--wasm-map-dir=", 15) ? a + 15
+ : NULL);
+ if (!v) return -1;
+ if (wasm_list_push(tool, o->map_dirs, o->argv_bound, &o->nmap_dirs, v) != 0)
+ return -1;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-map-file") ||
+ driver_strneq(a, "--wasm-map-file=", 16)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-map-file",
+ driver_strneq(a, "--wasm-map-file=", 16) ? a + 16
+ : NULL);
+ if (!v) return -1;
+ if (wasm_list_push(tool, o->map_files, o->argv_bound, &o->nmap_files, v) !=
+ 0)
+ return -1;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-cwd") || driver_strneq(a, "--wasm-cwd=", 11)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-cwd",
+ driver_strneq(a, "--wasm-cwd=", 11) ? a + 11 : NULL);
+ if (!v) return -1;
+ o->cwd = v;
+ return 1;
+ }
+ if (driver_streq(a, "--wasm-stdio") ||
+ driver_strneq(a, "--wasm-stdio=", 13)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-stdio",
+ driver_strneq(a, "--wasm-stdio=", 13) ? a + 13
+ : NULL);
+ if (!v) return -1;
+ return wasm_set_stdio_mode(tool, o, v) == 0 ? 1 : -1;
+ }
+ if (driver_streq(a, "--wasm-clock") ||
+ driver_strneq(a, "--wasm-clock=", 13)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-clock",
+ driver_strneq(a, "--wasm-clock=", 13) ? a + 13
+ : NULL);
+ if (!v) return -1;
+ return wasm_set_clock_mode(tool, o, v) == 0 ? 1 : -1;
+ }
+ if (driver_streq(a, "--wasm-random") ||
+ driver_strneq(a, "--wasm-random=", 14)) {
+ v = wasm_take_value(tool, argc, argv, index, "--wasm-random",
+ driver_strneq(a, "--wasm-random=", 14) ? a + 14
+ : NULL);
+ if (!v) return -1;
+ return wasm_set_random_mode(tool, o, v) == 0 ? 1 : -1;
+ }
+
+ driver_errf(tool, "unknown wasm sandbox flag: %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(a)));
+ return -1;
+}
+
+int driver_wasm_run_options_used(const DriverWasmRunOptions* o) {
+ return o && o->used;
+}
+
+static int wasm_page_bytes(const char* tool, uint64_t pages, uint64_t* out) {
+ if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) {
+ driver_errf(tool, "wasm memory size overflows");
+ return 1;
+ }
+ *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
+ return 0;
+}
+
+static int wasm_preflight_instance(const DriverWasmRunOptions* opts,
+ const char* tool, KitJit* jit) {
+ KitWasmRuntimeLayout layout;
+ KitStatus st = kit_wasm_get_runtime_layout(jit, &layout);
+ uint64_t instance_bytes;
+ uint64_t total_memory_bytes = 0;
+ uint32_t i;
+ if (st != KIT_OK) {
+ driver_errf(tool, st == KIT_NOT_FOUND
+ ? "wasm runtime layout metadata missing"
+ : "wasm runtime layout metadata malformed");
+ return 1;
+ }
+ if (layout.nmemories > opts->max_memories) {
+ driver_errf(tool, "wasm memory count exceeds %u", opts->max_memories);
+ return 1;
+ }
+ instance_bytes = layout.instance_size ? layout.instance_size : 1u;
+ if (instance_bytes > opts->max_instance_bytes) {
+ driver_errf(tool, "wasm instance too large: %llu bytes",
+ (unsigned long long)instance_bytes);
+ return 1;
+ }
+ for (i = 0; i < layout.nmemories; ++i) {
+ const KitWasmMemoryLayout* ml = &layout.memories[i];
+ uint64_t mem_bytes;
+ if (ml->max_pages < ml->min_pages) {
+ driver_errf(tool, "wasm memory maximum below minimum");
+ return 1;
+ }
+ if (wasm_page_bytes(tool, ml->max_pages, &mem_bytes) != 0) return 1;
+ if (mem_bytes > opts->max_total_memory_bytes ||
+ total_memory_bytes > opts->max_total_memory_bytes - mem_bytes) {
+ driver_errf(tool, "wasm linear memory reservation exceeds %llu bytes",
+ (unsigned long long)opts->max_total_memory_bytes);
+ return 1;
+ }
+ total_memory_bytes += mem_bytes;
+ }
+ return 0;
+}
+
+/* Canned host import for the wasm-front test suite. Active only when the
+ * caller explicitly selects --wasm-imports=test. */
+int32_t driver_wasm_test_host_add(KitWasmInstance* inst, int32_t a, int32_t b) {
+ (void)inst;
+ return a + b;
+}
+
+static int wasm_host_add_type(const KitWasmImportType* type) {
+ return type && type->nparams == 2u && type->nresults == 1u &&
+ type->params[0] == KIT_WASM_VAL_I32 &&
+ type->params[1] == KIT_WASM_VAL_I32 &&
+ type->results[0] == KIT_WASM_VAL_I32;
+}
+
+static void* wasm_test_resolve(void* user, const char* module,
+ const char* field,
+ const KitWasmImportType* type) {
+ (void)user;
+ if (!module || !field) return NULL;
+ if (driver_streq(module, "env") && driver_streq(field, "host_add") &&
+ wasm_host_add_type(type))
+ return (void*)(uintptr_t)driver_wasm_test_host_add;
+ return NULL;
+}
+
+static const char* wasm_status_name(KitStatus st) {
+ switch (st) {
+ case KIT_OK:
+ return "ok";
+ case KIT_NOMEM:
+ return "out of memory";
+ case KIT_INVALID:
+ return "invalid wasm host configuration";
+ case KIT_UNSUPPORTED:
+ return "wasm host import kind unsupported";
+ case KIT_MALFORMED:
+ return "wasm host import metadata malformed";
+ case KIT_NOT_FOUND:
+ return "wasm host import unresolved";
+ case KIT_IO:
+ return "wasm host I/O failed";
+ case KIT_ERR:
+ case KIT_AMBIGUOUS:
+ default:
+ return "wasm host setup failed";
+ }
+}
+
+static int wasm_build_add_owned(DriverWasmHostBuild* b, const char* data,
+ size_t len, const char** out) {
+ char* p;
+ if (b->nowned >= b->owned_cap) return 1;
+ p = (char*)driver_alloc(b->env, len + 1u);
+ if (!p) return 1;
+ if (len) driver_memcpy(p, data, len);
+ p[len] = '\0';
+ b->owned[b->nowned].ptr = p;
+ b->owned[b->nowned].size = len + 1u;
+ b->nowned++;
+ *out = p;
+ return 0;
+}
+
+static int wasm_env_name_valid(const char* name) {
+ return name && name[0] && !driver_strchr(name, '=');
+}
+
+static int wasm_env_set_valid(const char* entry) {
+ const char* eq = driver_strchr(entry, '=');
+ return entry && entry[0] && eq && eq != entry;
+}
+
+static int wasm_build_env_pass(DriverWasmHostBuild* b, const char* name,
+ const char** out) {
+ const char* value;
+ size_t name_len;
+ size_t value_len;
+ char* p;
+ if (!wasm_env_name_valid(name)) {
+ driver_errf(b->tool, "--wasm-env-pass requires NAME");
+ return 1;
+ }
+ value = driver_getenv(name);
+ if (!value) {
+ *out = NULL;
+ return 0;
+ }
+ if (b->nowned >= b->owned_cap) return 1;
+ name_len = driver_strlen(name);
+ value_len = driver_strlen(value);
+ if (name_len > SIZE_MAX - value_len - 2u) return 1;
+ p = (char*)driver_alloc(b->env, name_len + value_len + 2u);
+ if (!p) return 1;
+ driver_memcpy(p, name, name_len);
+ p[name_len] = '=';
+ driver_memcpy(p + name_len + 1u, value, value_len);
+ p[name_len + 1u + value_len] = '\0';
+ b->owned[b->nowned].ptr = p;
+ b->owned[b->nowned].size = name_len + value_len + 2u;
+ b->nowned++;
+ *out = p;
+ return 0;
+}
+
+static uint32_t wasm_process_env_count(void) {
+ const char* const* envp = driver_environ();
+ uint32_t n = 0;
+ if (!envp) return 0;
+ while (envp[n]) {
+ if (n == UINT32_MAX) break;
+ ++n;
+ }
+ return n;
+}
+
+static int wasm_build_env(DriverWasmHostBuild* b, KitWasmHostConfig* cfg) {
+ const DriverWasmRunOptions* o = b->opts;
+ uint32_t inherited = o->env_mode == DRIVER_WASM_ENV_INHERIT
+ ? wasm_process_env_count()
+ : 0u;
+ uint64_t cap64 = (uint64_t)inherited + o->nenv_pass + o->nenv_set;
+ uint32_t n = 0;
+ uint32_t i;
+ if (cap64 > UINT32_MAX) {
+ driver_errf(b->tool, "too many wasm environment entries");
+ return 1;
+ }
+ b->env_cap = (uint32_t)cap64;
+ if (b->env_cap) {
+ b->env_entries = (const char**)driver_alloc_zeroed(
+ b->env, (size_t)b->env_cap * sizeof(*b->env_entries));
+ if (!b->env_entries) {
+ driver_errf(b->tool, "out of memory");
+ return 1;
+ }
+ }
+ if (inherited) {
+ const char* const* envp = driver_environ();
+ for (i = 0; i < inherited; ++i) b->env_entries[n++] = envp[i];
+ }
+ for (i = 0; i < o->nenv_pass; ++i) {
+ const char* entry = NULL;
+ if (wasm_build_env_pass(b, o->env_pass[i], &entry) != 0) {
+ driver_errf(b->tool, "out of memory");
+ return 1;
+ }
+ if (entry) b->env_entries[n++] = entry;
+ }
+ for (i = 0; i < o->nenv_set; ++i) {
+ if (!wasm_env_set_valid(o->env_set[i])) {
+ driver_errf(b->tool, "--wasm-env-set requires NAME=VALUE");
+ return 1;
+ }
+ b->env_entries[n++] = o->env_set[i];
+ }
+ b->nenv_entries = n;
+ cfg->env = b->env_entries;
+ cfg->nenv = n;
+ return 0;
+}
+
+static const char* wasm_find_eq(const char* s) {
+ while (s && *s) {
+ if (*s == '=') return s;
+ ++s;
+ }
+ return NULL;
+}
+
+static int wasm_suffix_mode_len(const char* s, size_t len, uint32_t* flags,
+ size_t* new_len) {
+ if (len >= 3u && s[len - 3u] == ':' && s[len - 2u] == 'r' &&
+ s[len - 1u] == 'o') {
+ *flags = KIT_WASM_FS_READ;
+ *new_len = len - 3u;
+ return 0;
+ }
+ if (len >= 3u && s[len - 3u] == ':' && s[len - 2u] == 'r' &&
+ s[len - 1u] == 'w') {
+ *flags = KIT_WASM_FS_READ | KIT_WASM_FS_WRITE;
+ *new_len = len - 3u;
+ return 0;
+ }
+ *flags = KIT_WASM_FS_READ;
+ *new_len = len;
+ return 0;
+}
+
+static int wasm_build_one_mount(DriverWasmHostBuild* b, const char* spec,
+ uint32_t extra_flags,
+ KitWasmFsMount* out) {
+ const char* eq = wasm_find_eq(spec);
+ const char* host;
+ const char* guest;
+ size_t host_len;
+ size_t guest_len;
+ uint32_t flags;
+ if (!eq || eq == spec || !eq[1]) {
+ driver_errf(b->tool, "wasm filesystem maps require HOST=GUEST[:ro|rw]");
+ return 1;
+ }
+ host = spec;
+ host_len = (size_t)(eq - spec);
+ guest = eq + 1;
+ guest_len = driver_strlen(guest);
+ wasm_suffix_mode_len(guest, guest_len, &flags, &guest_len);
+ if (guest_len == 0 || guest[0] != '/') {
+ driver_errf(b->tool, "wasm guest paths must be absolute");
+ return 1;
+ }
+ if (wasm_build_add_owned(b, host, host_len, &out->host_path) != 0 ||
+ wasm_build_add_owned(b, guest, guest_len, &out->guest_path) != 0) {
+ driver_errf(b->tool, "out of memory");
+ return 1;
+ }
+ out->flags = flags | extra_flags;
+ return 0;
+}
+
+static int wasm_build_mounts(DriverWasmHostBuild* b, KitWasmHostConfig* cfg) {
+ const DriverWasmRunOptions* o = b->opts;
+ uint64_t total = (uint64_t)o->nmap_dirs + o->nmap_files;
+ uint32_t n = 0;
+ uint32_t i;
+ if (total > UINT32_MAX) {
+ driver_errf(b->tool, "too many wasm filesystem maps");
+ return 1;
+ }
+ b->mounts_cap = (uint32_t)total;
+ if (b->mounts_cap) {
+ b->mounts = (KitWasmFsMount*)driver_alloc_zeroed(
+ b->env, (size_t)b->mounts_cap * sizeof(*b->mounts));
+ if (!b->mounts) {
+ driver_errf(b->tool, "out of memory");
+ return 1;
+ }
+ }
+ for (i = 0; i < o->nmap_dirs; ++i) {
+ if (wasm_build_one_mount(b, o->map_dirs[i], 0u, &b->mounts[n]) != 0)
+ return 1;
+ ++n;
+ }
+ for (i = 0; i < o->nmap_files; ++i) {
+ if (wasm_build_one_mount(b, o->map_files[i], KIT_WASM_FS_FILE,
+ &b->mounts[n]) != 0)
+ return 1;
+ ++n;
+ }
+ b->nmounts = n;
+ cfg->mounts = b->mounts;
+ cfg->nmounts = n;
+ return 0;
+}
+
+static KitStatus wasm_driver_write(void* user, uint32_t fd,
+ const uint8_t* data, size_t n,
+ size_t* nwritten_out) {
+ DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
+ KitWriter* w = fd == 2u ? driver_stderr_writer(b->env)
+ : driver_stdout_writer(b->env);
+ KitStatus st;
+ if (!w) return KIT_NOMEM;
+ st = kit_writer_write(w, data, n);
+ if (st == KIT_OK) st = kit_writer_status(w);
+ kit_writer_close(w);
+ if (nwritten_out) *nwritten_out = st == KIT_OK ? n : 0u;
+ return st;
+}
+
+static KitStatus wasm_driver_clock(void* user, uint32_t clock_id,
+ uint64_t* ns_out) {
+ DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
+ if (b->opts->clock_mode == DRIVER_WASM_CLOCK_MONOTONIC && clock_id == 1u) {
+ *ns_out = driver_now_ns();
+ return KIT_OK;
+ }
+ if (b->opts->clock_mode == DRIVER_WASM_CLOCK_REALTIME && clock_id == 0u &&
+ b->env->now >= 0) {
+ *ns_out = (uint64_t)b->env->now * 1000000000ull;
+ return KIT_OK;
+ }
+ return KIT_UNSUPPORTED;
+}
+
+static uint64_t wasm_seed_hash(const char* s) {
+ uint64_t h = 1469598103934665603ull;
+ if (!s || !*s) return 0x9e3779b97f4a7c15ull;
+ while (*s) {
+ h ^= (uint8_t)*s++;
+ h *= 1099511628211ull;
+ }
+ return h ? h : 0x9e3779b97f4a7c15ull;
+}
+
+static uint64_t wasm_seed_next(DriverWasmHostBuild* b) {
+ uint64_t x = b->random_state;
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ b->random_state = x;
+ return x * 2685821657736338717ull;
+}
+
+static KitStatus wasm_driver_random(void* user, uint8_t* dst, size_t n) {
+ DriverWasmHostBuild* b = (DriverWasmHostBuild*)user;
+ size_t i;
+ if (b->opts->random_mode == DRIVER_WASM_RANDOM_HOST)
+ return driver_random_bytes(dst, n) == 0 ? KIT_OK : KIT_ERR;
+ if (b->opts->random_mode != DRIVER_WASM_RANDOM_SEED) return KIT_UNSUPPORTED;
+ for (i = 0; i < n; ++i) {
+ if ((i & 7u) == 0) {
+ uint64_t x = wasm_seed_next(b);
+ size_t j;
+ for (j = 0; j < 8u && i + j < n; ++j)
+ dst[i + j] = (uint8_t)(x >> (j * 8u));
+ }
+ }
+ return KIT_OK;
+}
+
+static void wasm_build_release(DriverWasmHostBuild* b) {
+ uint32_t i;
+ if (!b) return;
+ if (b->host) kit_wasm_host_free(b->host);
+ for (i = 0; i < b->nowned; ++i) {
+ if (b->owned[i].ptr) driver_free(b->env, b->owned[i].ptr, b->owned[i].size);
+ }
+ if (b->owned)
+ driver_free(b->env, b->owned, (size_t)b->owned_cap * sizeof(*b->owned));
+ if (b->mounts)
+ driver_free(b->env, b->mounts,
+ (size_t)b->mounts_cap * sizeof(*b->mounts));
+ if (b->env_entries) {
+ driver_free(b->env, b->env_entries,
+ (size_t)b->env_cap * sizeof(*b->env_entries));
+ }
+ memset(b, 0, sizeof *b);
+}
+
+static int wasm_build_host(const DriverWasmRunOptions* opts, const char* tool,
+ DriverWasmHostBuild* b) {
+ KitWasmHostConfig cfg;
+ uint64_t owned_cap64;
+ KitStatus st;
+ memset(b, 0, sizeof *b);
+ memset(&cfg, 0, sizeof cfg);
+ b->env = opts->env;
+ b->opts = opts;
+ b->tool = tool;
+ owned_cap64 = (uint64_t)opts->nenv_pass +
+ ((uint64_t)opts->nmap_dirs + opts->nmap_files) * 2u;
+ if (owned_cap64 > UINT32_MAX) {
+ driver_errf(tool, "too many wasm sandbox strings");
+ return 1;
+ }
+ b->owned_cap = (uint32_t)owned_cap64;
+ if (b->owned_cap) {
+ b->owned = (DriverWasmOwnedString*)driver_alloc_zeroed(
+ opts->env, (size_t)b->owned_cap * sizeof(*b->owned));
+ if (!b->owned) {
+ driver_errf(tool, "out of memory");
+ return 1;
+ }
+ }
+
+ cfg.heap = opts->env->heap;
+ cfg.flags = opts->imports == DRIVER_WASM_IMPORT_WASI
+ ? KIT_WASM_HOST_WASI_PREVIEW1
+ : 0u;
+ cfg.max_instance_bytes = opts->max_instance_bytes;
+ cfg.max_total_memory_bytes = opts->max_total_memory_bytes;
+ cfg.max_memories = opts->max_memories;
+ cfg.args = opts->args;
+ cfg.nargs = opts->nargs;
+ cfg.cwd = opts->cwd ? opts->cwd : "/";
+ cfg.file_io = &opts->env->file_io;
+ cfg.user = b;
+ if (opts->stdio_mode == DRIVER_WASM_STDIO_INHERIT)
+ cfg.write = wasm_driver_write;
+ if (opts->clock_mode != DRIVER_WASM_CLOCK_NONE) cfg.clock = wasm_driver_clock;
+ if (opts->random_mode != DRIVER_WASM_RANDOM_NONE) {
+ cfg.random = wasm_driver_random;
+ b->random_state = wasm_seed_hash(opts->random_seed);
+ }
+ if (wasm_build_env(b, &cfg) != 0 || wasm_build_mounts(b, &cfg) != 0)
+ return 1;
+ st = kit_wasm_host_new(&cfg, &b->host);
+ if (st != KIT_OK) {
+ driver_errf(tool, "%s", wasm_status_name(st));
+ return 1;
+ }
+ return 0;
+}
+
+static int wasm_bind_imports(const DriverWasmRunOptions* opts,
+ const char* tool, KitCompiler* compiler,
+ KitJit* jit, DriverWasmHostBuild* build,
+ KitWasmInstance* inst) {
+ KitStatus st;
+ if (opts->imports == DRIVER_WASM_IMPORT_TEST) {
+ st = kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0,
+ wasm_test_resolve, NULL);
+ } else if (opts->imports == DRIVER_WASM_IMPORT_WASI) {
+ st = kit_wasm_host_bind_imports(build->host, compiler, jit, inst);
+ } else {
+ st = kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0, NULL, NULL);
+ }
+ if (st != KIT_OK) {
+ driver_errf(tool, "%s", wasm_status_name(st));
+ return 1;
+ }
+ return 0;
+}
+
+static int wasm_prepare_instance(const DriverWasmRunOptions* opts,
+ const char* tool, KitCompiler* compiler,
+ KitJit* jit, DriverWasmHostBuild* build,
+ KitWasmInstance** out) {
+ KitStatus st;
+ *out = NULL;
+ if (wasm_preflight_instance(opts, tool, jit) != 0) return 1;
+ if (wasm_build_host(opts, tool, build) != 0) return 1;
+ st = kit_wasm_instance_new(build->host, jit, out);
+ if (st != KIT_OK) {
+ driver_errf(tool, "%s", wasm_status_name(st));
+ return 1;
+ }
+ if (wasm_bind_imports(opts, tool, compiler, jit, build, *out) != 0) {
+ kit_wasm_instance_free(*out);
+ *out = NULL;
+ return 1;
+ }
+ return 0;
+}
+
+int driver_wasm_run_call_entry(const DriverWasmRunOptions* opts,
+ const char* tool, KitCompiler* compiler,
+ KitJit* jit, void* entry, int* rc_out) {
+ void* init_sym = kit_jit_lookup(jit, KIT_SLICE_LIT("__kit_wasm_init"));
+ DriverWasmHostBuild build;
+ KitWasmInstance* inst = NULL;
+ union {
+ void* p;
+ DriverWasmInitFn fn;
+ } init_u;
+ union {
+ void* p;
+ DriverWasmMainFn fn;
+ } entry_u;
+ if (!init_sym) return 0;
+ memset(&build, 0, sizeof build);
+ if (wasm_prepare_instance(opts, tool, compiler, jit, &build, &inst) != 0) {
+ *rc_out = 1;
+ wasm_build_release(&build);
+ return 1;
+ }
+ init_u.p = init_sym;
+ entry_u.p = entry;
+ init_u.fn(inst);
+ *rc_out = entry_u.fn(inst);
+ (void)kit_wasm_instance_exit_code(inst, rc_out);
+ kit_wasm_instance_free(inst);
+ wasm_build_release(&build);
+ return 1;
+}
+
+int driver_wasm_run_call_entry_interp(const DriverWasmRunOptions* opts,
+ const char* tool, KitCompiler* compiler,
+ KitJit* jit, KitInterpProgram* interp,
+ const char* entry_name, int* rc_out) {
+ KitInterpFunc* init_fn =
+ kit_interp_lookup(interp, KIT_SLICE_LIT("__kit_wasm_init"));
+ KitInterpFunc* entry_fn =
+ kit_interp_lookup(interp, kit_slice_cstr(entry_name));
+ DriverWasmHostBuild build;
+ KitWasmInstance* inst = NULL;
+ uint64_t args[1];
+ int64_t ret = 0;
+ KitInterpStatus s;
+ if (!init_fn) return 0;
+ if (!entry_fn) {
+ driver_errf(tool, "interp: wasm entry %.*s has no interpretable IR",
+ KIT_SLICE_ARG(kit_slice_cstr(entry_name)));
+ *rc_out = 1;
+ return 1;
+ }
+ memset(&build, 0, sizeof build);
+ if (wasm_prepare_instance(opts, tool, compiler, jit, &build, &inst) != 0) {
+ *rc_out = 1;
+ wasm_build_release(&build);
+ return 1;
+ }
+ args[0] = (uint64_t)(uintptr_t)inst;
+ s = kit_interp_call_args(interp, init_fn, args, 1u, &ret);
+ if (s == KIT_INTERP_DONE)
+ s = kit_interp_call_args(interp, entry_fn, args, 1u, &ret);
+ if (s == KIT_INTERP_DONE) {
+ *rc_out = (int)ret;
+ (void)kit_wasm_instance_exit_code(inst, rc_out);
+ } else {
+ driver_errf(tool, "interp: could not execute wasm entry %.*s",
+ KIT_SLICE_ARG(kit_slice_cstr(entry_name)));
+ *rc_out = 1;
+ }
+ kit_wasm_instance_free(inst);
+ wasm_build_release(&build);
+ return 1;
+}
diff --git a/driver/lib/wasm_run.h b/driver/lib/wasm_run.h
@@ -0,0 +1,91 @@
+#ifndef KIT_DRIVER_WASM_RUN_H
+#define KIT_DRIVER_WASM_RUN_H
+
+#include <kit/interp.h>
+#include <kit/jit.h>
+#include <stdint.h>
+
+#include "env.h"
+
+typedef enum DriverWasmImportMode {
+ DRIVER_WASM_IMPORT_DENY = 0,
+ DRIVER_WASM_IMPORT_WASI = 1,
+ DRIVER_WASM_IMPORT_TEST = 2,
+} DriverWasmImportMode;
+
+typedef enum DriverWasmEnvMode {
+ DRIVER_WASM_ENV_NONE = 0,
+ DRIVER_WASM_ENV_ALLOWLIST = 1,
+ DRIVER_WASM_ENV_INHERIT = 2,
+} DriverWasmEnvMode;
+
+typedef enum DriverWasmStdioMode {
+ DRIVER_WASM_STDIO_NULL = 0,
+ DRIVER_WASM_STDIO_INHERIT = 1,
+} DriverWasmStdioMode;
+
+typedef enum DriverWasmClockMode {
+ DRIVER_WASM_CLOCK_NONE = 0,
+ DRIVER_WASM_CLOCK_MONOTONIC = 1,
+ DRIVER_WASM_CLOCK_REALTIME = 2,
+} DriverWasmClockMode;
+
+typedef enum DriverWasmRandomMode {
+ DRIVER_WASM_RANDOM_NONE = 0,
+ DRIVER_WASM_RANDOM_HOST = 1,
+ DRIVER_WASM_RANDOM_SEED = 2,
+} DriverWasmRandomMode;
+
+typedef struct DriverWasmRunOptions {
+ DriverEnv* env;
+ size_t argv_bound;
+ int used;
+
+ uint64_t max_instance_bytes;
+ uint64_t max_total_memory_bytes;
+ uint32_t max_memories;
+
+ uint8_t imports; /* DriverWasmImportMode */
+ uint8_t env_mode;
+ uint8_t stdio_mode;
+ uint8_t clock_mode;
+ uint8_t random_mode;
+ uint8_t reserved[3];
+
+ const char** env_pass;
+ const char** env_set;
+ const char** map_dirs;
+ const char** map_files;
+ uint32_t nenv_pass;
+ uint32_t nenv_set;
+ uint32_t nmap_dirs;
+ uint32_t nmap_files;
+ const char* const* args;
+ uint32_t nargs;
+ const char* cwd;
+ const char* random_seed;
+} DriverWasmRunOptions;
+
+int driver_wasm_run_options_init(DriverWasmRunOptions*, DriverEnv*,
+ size_t argv_bound);
+void driver_wasm_run_options_fini(DriverWasmRunOptions*);
+
+/* Try to consume argv[*index] as a Wasm sandbox option.
+ * Returns 1 on consume, 0 when the flag is unrelated, and -1 on usage error. */
+int driver_wasm_run_try_consume(DriverWasmRunOptions*, const char* tool,
+ int argc, char** argv, int* index);
+
+int driver_wasm_run_options_used(const DriverWasmRunOptions*);
+
+/* Returns 0 when the image is not a kit-lowered Wasm module. Otherwise runs
+ * the Wasm init + entry sequence, stores the result in *rc_out, and returns 1. */
+int driver_wasm_run_call_entry(const DriverWasmRunOptions*, const char* tool,
+ KitCompiler*, KitJit*, void* entry,
+ int* rc_out);
+
+int driver_wasm_run_call_entry_interp(const DriverWasmRunOptions*,
+ const char* tool, KitCompiler*, KitJit*,
+ KitInterpProgram*, const char* entry_name,
+ int* rc_out);
+
+#endif
diff --git a/include/kit/wasm.h b/include/kit/wasm.h
@@ -38,6 +38,7 @@ extern "C" {
* tables, and lowered global slots. Treat as a raw buffer at the public
* API surface; the binder uses module-emitted metadata to locate slots. */
typedef struct KitWasmInstance KitWasmInstance;
+typedef struct KitWasmHost KitWasmHost;
typedef enum KitWasmValType {
KIT_WASM_VAL_I32 = 0,
@@ -113,6 +114,53 @@ typedef struct KitWasmRuntimeLayout {
} KitWasmRuntimeLayout;
#endif
+typedef enum KitWasmHostFlags {
+ KIT_WASM_HOST_WASI_PREVIEW1 = 1u << 0,
+} KitWasmHostFlags;
+
+typedef enum KitWasmFsFlags {
+ KIT_WASM_FS_READ = 1u << 0,
+ KIT_WASM_FS_WRITE = 1u << 1,
+ KIT_WASM_FS_FILE = 1u << 2,
+} KitWasmFsFlags;
+
+typedef struct KitWasmFsMount {
+ const char* host_path; /* host path exposed to the guest */
+ const char* guest_path; /* guest absolute path, e.g. "/work" */
+ uint32_t flags; /* KIT_WASM_FS_* */
+} KitWasmFsMount;
+
+typedef KitStatus (*KitWasmWriteFn)(void* user, uint32_t fd,
+ const uint8_t* data, size_t n,
+ size_t* nwritten_out);
+typedef KitStatus (*KitWasmClockFn)(void* user, uint32_t clock_id,
+ uint64_t* ns_out);
+typedef KitStatus (*KitWasmRandomFn)(void* user, uint8_t* dst, size_t n);
+
+typedef struct KitWasmHostConfig {
+ KitHeap* heap;
+ uint32_t flags; /* KIT_WASM_HOST_* */
+
+ uint64_t max_instance_bytes;
+ uint64_t max_total_memory_bytes;
+ uint32_t max_memories;
+
+ const char* const* args;
+ uint32_t nargs;
+ const char* const* env;
+ uint32_t nenv;
+
+ const KitWasmFsMount* mounts;
+ uint32_t nmounts;
+ const char* cwd;
+ const KitFileIO* file_io;
+
+ KitWasmWriteFn write;
+ KitWasmClockFn clock;
+ KitWasmRandomFn random;
+ void* user;
+} KitWasmHostConfig;
+
/* Static host-import descriptor.
*
* `func` must match the declared wasm type when called with the kit-
@@ -167,6 +215,25 @@ KIT_API void kit_wasm_get_host_imports(KitCompiler*,
KIT_API KitStatus kit_wasm_get_runtime_layout(KitJit* jit,
KitWasmRuntimeLayout* out);
+/* Create a configurable host shim and allocate/free instances with sandbox
+ * limits enforced before any module code runs. The config is shallow-copied:
+ * string arrays, mount arrays, callbacks, and file_io are borrowed and must
+ * outlive host use. */
+KIT_API KitStatus kit_wasm_host_new(const KitWasmHostConfig* config,
+ KitWasmHost** out);
+KIT_API void kit_wasm_host_free(KitWasmHost*);
+KIT_API KitStatus kit_wasm_instance_new(KitWasmHost*, KitJit*,
+ KitWasmInstance** out);
+KIT_API void kit_wasm_instance_free(KitWasmInstance*);
+
+/* Bind the host's configured imports, currently WASI Preview1 when
+ * KIT_WASM_HOST_WASI_PREVIEW1 is set. */
+KIT_API KitStatus kit_wasm_host_bind_imports(KitWasmHost*, KitCompiler*,
+ KitJit*, KitWasmInstance*);
+
+/* Nonzero when a WASI proc_exit import was called. */
+KIT_API int kit_wasm_instance_exit_code(KitWasmInstance*, int* code_out);
+
/* Resolve every declared import in the module loaded into `jit` against
* the supplied static table and/or resolver, and write the resolved
* function pointers into the per-import slots of `inst`.
diff --git a/mk/driver_srcs.mk b/mk/driver_srcs.mk
@@ -55,6 +55,7 @@ DRIVER_SRCS += $(call need-any,CC CHECK LD RUN,driver/lib/lib_resolve.c)
DRIVER_SRCS += $(call need-any,CC CHECK RUN,driver/lib/hosted.c)
DRIVER_SRCS += $(call need-any,CC CHECK LD,driver/lib/runtime.c)
DRIVER_SRCS += $(call need-any,AR RANLIB STRIP DBG RUN,driver/lib/inputs.c)
+DRIVER_SRCS += $(call need-any,RUN,driver/lib/wasm_run.c)
DRIVER_SRCS += $(call need-any,CC CHECK COMPILE,driver/lib/compile_engine.c)
DRIVER_SRCS += $(call need-any,CAS PKG,driver/lib/dist_host.c)
diff --git a/mk/test.mk b/mk/test.mk
@@ -69,6 +69,7 @@ TEST_TARGETS = \
test-driver-pkg \
test-driver-strings \
test-driver-tools \
+ test-driver-wasm \
test-hash \
test-driver-strip \
test-dwarf \
@@ -186,7 +187,7 @@ test-images:
test-cf-corpus-selftest:
@bash test/lib/kit_corpus_selftest.sh
-test-driver: test-driver-cc test-driver-compile test-driver-ar test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools
+test-driver: test-driver-cc test-driver-compile test-driver-ar test-driver-cas test-driver-strip test-driver-objcopy test-driver-objdump test-driver-pkg test-driver-strings test-driver-tools test-driver-wasm
test-driver-cc: bin
@KIT=$(abspath $(BIN)) sh test/driver/run.sh
@@ -272,6 +273,9 @@ test-driver-strings: bin
test-driver-tools: bin
@KIT=$(abspath $(BIN)) sh test/tools/run.sh
+test-driver-wasm: bin
+ @KIT=$(abspath $(BIN)) sh test/driver-wasm/run.sh
+
# DWARF consumer unit test: builds a hand-crafted DWARF-bearing ELF in
# memory and exercises every kit_dwarf_* entry. It reaches into the
# internal object builder to synthesize the fixture, so link individual
diff --git a/src/abi/abi.h b/src/abi/abi.h
@@ -124,6 +124,10 @@ typedef struct ABIFuncInfo {
* Apple ARM64 sets this; AAPCS64 / SysV-x64 leave it 0 (variadics
* use the same register routing as fixed args). */
u8 vararg_on_stack;
+ /* Minimum stack slot size/alignment for scalar stack-passed arguments.
+ * Zero means the backend default. Apple ARM64 uses 4-byte compact slots for
+ * stack arguments such as int32; AAPCS64 uses 8-byte slots. */
+ u8 stack_arg_min_align;
u32 vararg_gp_offset;
u32 vararg_fp_offset;
u32 vararg_overflow_offset;
diff --git a/src/abi/abi_apple_arm64.c b/src/abi/abi_apple_arm64.c
@@ -39,6 +39,7 @@ static ABIFuncInfo* apple_arm64_compute_func_info(TargetABI* a,
* target OS itself. */
ABIFuncInfo* info = aapcs64_compute_func_info(a, fn);
info->vararg_on_stack = 1;
+ info->stack_arg_min_align = 4;
return info;
}
diff --git a/src/api/wasm_host.c b/src/api/wasm_host.c
@@ -0,0 +1,816 @@
+#include <kit/wasm.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef struct KitWasmMemoryRecord {
+ uint8_t* data;
+ uint64_t pages;
+ uint64_t max_pages;
+ uint32_t flags;
+} KitWasmMemoryRecord;
+
+#define KIT_WASM_HOST_MAX_FDS 64u
+
+typedef struct KitWasmOpenFd {
+ uint32_t used;
+ KitFileData data;
+ const KitFileIO* io;
+ uint64_t pos;
+} KitWasmOpenFd;
+
+struct KitWasmHost {
+ KitWasmHostConfig config;
+};
+
+typedef struct KitWasmRuntimeInstance {
+ KitWasmHost* host;
+ const KitWasmMemoryLayout* memory_layouts;
+ uint64_t allocation_bytes;
+ uint64_t instance_bytes;
+ uint64_t total_memory_bytes;
+ uint8_t** memories;
+ uint64_t* memory_bytes;
+ uint32_t nmemories;
+ int exit_called;
+ int exit_code;
+ KitWasmOpenFd fds[KIT_WASM_HOST_MAX_FDS];
+ uint8_t instance[];
+} KitWasmRuntimeInstance;
+
+enum {
+ WASI_ESUCCESS = 0,
+ WASI_EACCES = 2,
+ WASI_EBADF = 8,
+ WASI_EINVAL = 28,
+ WASI_EIO = 29,
+ WASI_ENOMEM = 34,
+ WASI_ENOENT = 44,
+ WASI_ENOSYS = 52,
+ WASI_ENOTCAPABLE = 76,
+};
+
+static KitWasmRuntimeInstance* wasm_wrap_from_instance(KitWasmInstance* inst) {
+ return (KitWasmRuntimeInstance*)((uint8_t*)inst -
+ offsetof(KitWasmRuntimeInstance, instance));
+}
+
+static KitWasmInstance* wasm_instance_from_wrap(KitWasmRuntimeInstance* w) {
+ return (KitWasmInstance*)w->instance;
+}
+
+static size_t wasm_cstrlen(const char* s) {
+ size_t n = 0;
+ if (!s) return 0;
+ while (s[n]) ++n;
+ return n;
+}
+
+static int wasm_streq(const char* a, const char* b) {
+ size_t i = 0;
+ if (!a || !b) return 0;
+ while (a[i] && b[i]) {
+ if (a[i] != b[i]) return 0;
+ ++i;
+ }
+ return a[i] == b[i];
+}
+
+static KitStatus wasm_u64_to_size(uint64_t n, size_t* out) {
+ if (n > (uint64_t)SIZE_MAX) return KIT_INVALID;
+ *out = (size_t)n;
+ return KIT_OK;
+}
+
+static KitStatus wasm_page_bytes(uint64_t pages, uint64_t* out) {
+ if (pages > UINT64_MAX / (uint64_t)KIT_WASM_PAGE_SIZE) return KIT_INVALID;
+ *out = pages * (uint64_t)KIT_WASM_PAGE_SIZE;
+ return KIT_OK;
+}
+
+static uint32_t wasm_preopen_count(const KitWasmHost* host) {
+ return host ? host->config.nmounts : 0u;
+}
+
+static const KitWasmFsMount* wasm_preopen_for_fd(KitWasmRuntimeInstance* w,
+ uint32_t fd) {
+ uint32_t index;
+ if (!w || !w->host || fd < 3u) return NULL;
+ index = fd - 3u;
+ if (index >= w->host->config.nmounts) return NULL;
+ return &w->host->config.mounts[index];
+}
+
+static size_t wasm_guest_parent_len(const char* path) {
+ size_t i;
+ size_t last = 0;
+ if (!path || path[0] != '/') return 0;
+ for (i = 1; path[i]; ++i) {
+ if (path[i] == '/') last = i;
+ }
+ return last ? last : 1u;
+}
+
+static const char* wasm_guest_basename(const char* path) {
+ size_t i;
+ size_t last = 0;
+ if (!path) return NULL;
+ for (i = 0; path[i]; ++i) {
+ if (path[i] == '/') last = i + 1u;
+ }
+ return path + last;
+}
+
+static int wasm_bytes_eq_cstr(const uint8_t* a, uint32_t alen,
+ const char* b) {
+ uint32_t i;
+ if (!a || !b) return 0;
+ for (i = 0; i < alen; ++i) {
+ if (b[i] == '\0' || a[i] != (uint8_t)b[i]) return 0;
+ }
+ return b[alen] == '\0';
+}
+
+static KitWasmMemoryRecord* wasm_memory0(KitWasmRuntimeInstance* w) {
+ if (!w || w->nmemories == 0 || !w->memory_layouts) return NULL;
+ if (w->memory_layouts[0].offset > w->instance_bytes) return NULL;
+ if (w->instance_bytes - w->memory_layouts[0].offset <
+ sizeof(KitWasmMemoryRecord))
+ return NULL;
+ return (KitWasmMemoryRecord*)(w->instance + w->memory_layouts[0].offset);
+}
+
+static int wasm_mem_ptr(KitWasmInstance* inst, uint32_t addr, uint32_t n,
+ uint8_t** out) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ KitWasmMemoryRecord* mem = wasm_memory0(w);
+ uint64_t end;
+ uint64_t size;
+ if (!out || !mem || !mem->data) return 0;
+ if (addr > UINT32_MAX - n) return 0;
+ end = (uint64_t)addr + (uint64_t)n;
+ if (wasm_page_bytes(mem->pages, &size) != KIT_OK) return 0;
+ if (end > size) return 0;
+ *out = mem->data + addr;
+ return 1;
+}
+
+static int wasm_read_u32(KitWasmInstance* inst, uint32_t addr, uint32_t* out) {
+ uint8_t* p;
+ if (!wasm_mem_ptr(inst, addr, 4u, &p)) return 0;
+ *out = ((uint32_t)p[0]) | ((uint32_t)p[1] << 8) |
+ ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
+ return 1;
+}
+
+static int wasm_write_u32(KitWasmInstance* inst, uint32_t addr, uint32_t v) {
+ uint8_t* p;
+ if (!wasm_mem_ptr(inst, addr, 4u, &p)) return 0;
+ p[0] = (uint8_t)(v & 0xffu);
+ p[1] = (uint8_t)((v >> 8) & 0xffu);
+ p[2] = (uint8_t)((v >> 16) & 0xffu);
+ p[3] = (uint8_t)((v >> 24) & 0xffu);
+ return 1;
+}
+
+static int wasm_write_u64(KitWasmInstance* inst, uint32_t addr, uint64_t v) {
+ uint8_t* p;
+ if (!wasm_mem_ptr(inst, addr, 8u, &p)) return 0;
+ p[0] = (uint8_t)(v & 0xffu);
+ p[1] = (uint8_t)((v >> 8) & 0xffu);
+ p[2] = (uint8_t)((v >> 16) & 0xffu);
+ p[3] = (uint8_t)((v >> 24) & 0xffu);
+ p[4] = (uint8_t)((v >> 32) & 0xffu);
+ p[5] = (uint8_t)((v >> 40) & 0xffu);
+ p[6] = (uint8_t)((v >> 48) & 0xffu);
+ p[7] = (uint8_t)((v >> 56) & 0xffu);
+ return 1;
+}
+
+static int wasm_status_errno(KitStatus st) {
+ switch (st) {
+ case KIT_OK:
+ return WASI_ESUCCESS;
+ case KIT_NOMEM:
+ return WASI_ENOMEM;
+ case KIT_NOT_FOUND:
+ return WASI_ENOENT;
+ case KIT_IO:
+ return WASI_EIO;
+ case KIT_UNSUPPORTED:
+ return WASI_ENOSYS;
+ case KIT_INVALID:
+ case KIT_MALFORMED:
+ case KIT_AMBIGUOUS:
+ case KIT_ERR:
+ default:
+ return WASI_EINVAL;
+ }
+}
+
+static int wasm_sig(const KitWasmImportType* t, const KitWasmValType* params,
+ uint32_t nparams, const KitWasmValType* results,
+ uint32_t nresults) {
+ uint32_t i;
+ if (!t || t->nparams != nparams || t->nresults != nresults) return 0;
+ for (i = 0; i < nparams; ++i)
+ if (!t->params || t->params[i] != params[i]) return 0;
+ for (i = 0; i < nresults; ++i)
+ if (!t->results || t->results[i] != results[i]) return 0;
+ return 1;
+}
+
+static void wasi_proc_exit(KitWasmInstance* inst, int32_t code) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ w->exit_called = 1;
+ w->exit_code = code;
+}
+
+static int32_t wasi_fd_write(KitWasmInstance* inst, int32_t fd_i,
+ int32_t iovs_i, int32_t iovs_len_i,
+ int32_t nwritten_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint32_t fd = (uint32_t)fd_i;
+ uint32_t iovs = (uint32_t)iovs_i;
+ uint32_t iovs_len = (uint32_t)iovs_len_i;
+ uint32_t nwritten = (uint32_t)nwritten_i;
+ uint32_t total = 0;
+ uint32_t i;
+ if (fd != 1u && fd != 2u) return WASI_EBADF;
+ for (i = 0; i < iovs_len; ++i) {
+ uint32_t ptr;
+ uint32_t len;
+ uint8_t* data;
+ uint32_t off;
+ uint32_t rec;
+ size_t wrote = 0;
+ KitStatus st;
+ if (i > UINT32_MAX / 8u) return WASI_EINVAL;
+ off = i * 8u;
+ if (iovs > UINT32_MAX - off) return WASI_EINVAL;
+ rec = iovs + off;
+ if (!wasm_read_u32(inst, rec, &ptr) ||
+ !wasm_read_u32(inst, rec + 4u, &len) ||
+ !wasm_mem_ptr(inst, ptr, len, &data))
+ return WASI_EINVAL;
+ if (w->host->config.write) {
+ st = w->host->config.write(w->host->config.user, fd, data, len, &wrote);
+ if (st != KIT_OK) return wasm_status_errno(st);
+ if (wrote > len) wrote = len;
+ } else {
+ wrote = len;
+ }
+ if (total > UINT32_MAX - (uint32_t)wrote) return WASI_EINVAL;
+ total += (uint32_t)wrote;
+ if (wrote < len) break;
+ }
+ if (!wasm_write_u32(inst, nwritten, total)) return WASI_EINVAL;
+ return WASI_ESUCCESS;
+}
+
+static int wasm_strings_size(const char* const* items, uint32_t n,
+ uint32_t* bytes_out) {
+ uint64_t total = 0;
+ uint32_t i;
+ for (i = 0; i < n; ++i) {
+ total += (uint64_t)wasm_cstrlen(items ? items[i] : NULL) + 1u;
+ if (total > UINT32_MAX) return 0;
+ }
+ *bytes_out = (uint32_t)total;
+ return 1;
+}
+
+static int32_t wasi_environ_sizes_get(KitWasmInstance* inst, int32_t count_i,
+ int32_t size_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint32_t bytes;
+ if (!wasm_strings_size(w->host->config.env, w->host->config.nenv, &bytes))
+ return WASI_EINVAL;
+ if (!wasm_write_u32(inst, (uint32_t)count_i, w->host->config.nenv) ||
+ !wasm_write_u32(inst, (uint32_t)size_i, bytes))
+ return WASI_EINVAL;
+ return WASI_ESUCCESS;
+}
+
+static int wasm_write_strings(KitWasmInstance* inst, const char* const* items,
+ uint32_t nitems, uint32_t ptrs_addr,
+ uint32_t buf_addr) {
+ uint32_t total;
+ uint8_t* ptrs;
+ uint8_t* buf;
+ uint32_t off = 0;
+ uint32_t i;
+ if (!wasm_strings_size(items, nitems, &total)) return 0;
+ if (nitems > UINT32_MAX / 4u) return 0;
+ if (!wasm_mem_ptr(inst, ptrs_addr, nitems * 4u, &ptrs) ||
+ !wasm_mem_ptr(inst, buf_addr, total, &buf))
+ return 0;
+ (void)ptrs;
+ for (i = 0; i < nitems; ++i) {
+ const char* s = items ? items[i] : NULL;
+ size_t len = wasm_cstrlen(s);
+ if (len > (size_t)(UINT32_MAX - off - 1u)) return 0;
+ if (!wasm_write_u32(inst, ptrs_addr + i * 4u, buf_addr + off)) return 0;
+ if (len) memcpy(buf + off, s, len);
+ buf[off + (uint32_t)len] = 0;
+ off += (uint32_t)len + 1u;
+ }
+ return 1;
+}
+
+static int32_t wasi_environ_get(KitWasmInstance* inst, int32_t env_i,
+ int32_t buf_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ return wasm_write_strings(inst, w->host->config.env, w->host->config.nenv,
+ (uint32_t)env_i,
+ (uint32_t)buf_i)
+ ? WASI_ESUCCESS
+ : WASI_EINVAL;
+}
+
+static int32_t wasi_args_sizes_get(KitWasmInstance* inst, int32_t count_i,
+ int32_t size_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint32_t bytes;
+ if (!wasm_strings_size(w->host->config.args, w->host->config.nargs, &bytes))
+ return WASI_EINVAL;
+ if (!wasm_write_u32(inst, (uint32_t)count_i, w->host->config.nargs) ||
+ !wasm_write_u32(inst, (uint32_t)size_i, bytes))
+ return WASI_EINVAL;
+ return WASI_ESUCCESS;
+}
+
+static int32_t wasi_args_get(KitWasmInstance* inst, int32_t args_i,
+ int32_t buf_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ return wasm_write_strings(inst, w->host->config.args, w->host->config.nargs,
+ (uint32_t)args_i,
+ (uint32_t)buf_i)
+ ? WASI_ESUCCESS
+ : WASI_EINVAL;
+}
+
+static int32_t wasi_random_get(KitWasmInstance* inst, int32_t ptr_i,
+ int32_t len_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint8_t* dst;
+ KitStatus st;
+ if (!w->host->config.random) return WASI_ENOSYS;
+ if (!wasm_mem_ptr(inst, (uint32_t)ptr_i, (uint32_t)len_i, &dst))
+ return WASI_EINVAL;
+ st = w->host->config.random(w->host->config.user, dst, (size_t)len_i);
+ return wasm_status_errno(st);
+}
+
+static int32_t wasi_clock_time_get(KitWasmInstance* inst, int32_t clock_id_i,
+ int64_t precision_i, int32_t time_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint64_t ns = 0;
+ KitStatus st;
+ (void)precision_i;
+ if (!w->host->config.clock) return WASI_ENOSYS;
+ st = w->host->config.clock(w->host->config.user, (uint32_t)clock_id_i, &ns);
+ if (st != KIT_OK) return wasm_status_errno(st);
+ return wasm_write_u64(inst, (uint32_t)time_i, ns) ? WASI_ESUCCESS
+ : WASI_EINVAL;
+}
+
+static int32_t wasi_fd_prestat_get(KitWasmInstance* inst, int32_t fd_i,
+ int32_t prestat_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)fd_i);
+ uint8_t* p;
+ uint32_t len;
+ if (!m) return WASI_EBADF;
+ len = (m->flags & KIT_WASM_FS_FILE)
+ ? (uint32_t)wasm_guest_parent_len(m->guest_path)
+ : (uint32_t)wasm_cstrlen(m->guest_path);
+ if (!wasm_mem_ptr(inst, (uint32_t)prestat_i, 8u, &p)) return WASI_EINVAL;
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0;
+ p[4] = (uint8_t)(len & 0xffu);
+ p[5] = (uint8_t)((len >> 8) & 0xffu);
+ p[6] = (uint8_t)((len >> 16) & 0xffu);
+ p[7] = (uint8_t)((len >> 24) & 0xffu);
+ return WASI_ESUCCESS;
+}
+
+static int32_t wasi_fd_prestat_dir_name(KitWasmInstance* inst, int32_t fd_i,
+ int32_t path_i, int32_t path_len_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)fd_i);
+ uint8_t* dst;
+ size_t len;
+ if (!m) return WASI_EBADF;
+ len = (m->flags & KIT_WASM_FS_FILE) ? wasm_guest_parent_len(m->guest_path)
+ : wasm_cstrlen(m->guest_path);
+ if ((uint32_t)path_len_i < len) return WASI_EINVAL;
+ if (!wasm_mem_ptr(inst, (uint32_t)path_i, (uint32_t)len, &dst))
+ return WASI_EINVAL;
+ if (len) memcpy(dst, m->guest_path, len);
+ return WASI_ESUCCESS;
+}
+
+static int wasm_guest_path_safe(const uint8_t* path, uint32_t len) {
+ uint32_t i = 0;
+ if (!path || len == 0 || path[0] == '/') return 0;
+ while (i < len) {
+ uint32_t start = i;
+ while (i < len && path[i] != '/') {
+ if (path[i] == '\\') return 0;
+ ++i;
+ }
+ if (i == start) return 0;
+ if (i - start == 2u && path[start] == '.' && path[start + 1u] == '.')
+ return 0;
+ if (i < len) ++i;
+ }
+ return 1;
+}
+
+static KitStatus wasm_join_path(KitHeap* heap, const char* base,
+ const uint8_t* rel, uint32_t rel_len,
+ char** out, size_t* out_size) {
+ size_t base_len = wasm_cstrlen(base);
+ size_t need;
+ char* p;
+ int slash;
+ if (!base || !rel || !out || !out_size) return KIT_INVALID;
+ slash = base_len && base[base_len - 1u] != '/' && base[base_len - 1u] != '\\';
+ if (base_len > SIZE_MAX - (size_t)rel_len - (slash ? 2u : 1u))
+ return KIT_INVALID;
+ need = base_len + (slash ? 1u : 0u) + (size_t)rel_len + 1u;
+ p = (char*)heap->alloc(heap, need, 1u);
+ if (!p) return KIT_NOMEM;
+ if (base_len) memcpy(p, base, base_len);
+ if (slash) p[base_len++] = '/';
+ if (rel_len) memcpy(p + base_len, rel, rel_len);
+ p[base_len + (size_t)rel_len] = 0;
+ *out = p;
+ *out_size = need;
+ return KIT_OK;
+}
+
+static int wasm_find_free_fd(KitWasmRuntimeInstance* w, uint32_t* out) {
+ uint32_t start = 3u + wasm_preopen_count(w->host);
+ uint32_t fd;
+ if (start >= KIT_WASM_HOST_MAX_FDS) return 0;
+ for (fd = start; fd < KIT_WASM_HOST_MAX_FDS; ++fd) {
+ if (!w->fds[fd].used) {
+ *out = fd;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int32_t wasi_path_open(KitWasmInstance* inst, int32_t dirfd_i,
+ int32_t dirflags_i, int32_t path_i,
+ int32_t path_len_i, int32_t oflags_i,
+ int64_t rights_base_i,
+ int64_t rights_inheriting_i,
+ int32_t fdflags_i, int32_t opened_fd_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ const KitWasmFsMount* m = wasm_preopen_for_fd(w, (uint32_t)dirfd_i);
+ uint8_t* guest_path;
+ char* host_path = NULL;
+ size_t host_path_size = 0;
+ KitFileData data;
+ uint32_t fd;
+ KitStatus st;
+ (void)dirflags_i;
+ (void)rights_base_i;
+ (void)rights_inheriting_i;
+ (void)fdflags_i;
+ if (!m) return WASI_EBADF;
+ if (!(m->flags & KIT_WASM_FS_READ)) return WASI_EACCES;
+ if (oflags_i != 0) return WASI_ENOSYS;
+ if (!w->host->config.file_io || !w->host->config.file_io->read_all)
+ return WASI_ENOSYS;
+ if (!wasm_mem_ptr(inst, (uint32_t)path_i, (uint32_t)path_len_i,
+ &guest_path))
+ return WASI_EINVAL;
+ if (!wasm_guest_path_safe(guest_path, (uint32_t)path_len_i))
+ return WASI_ENOTCAPABLE;
+ if (m->flags & KIT_WASM_FS_FILE) {
+ const char* base = wasm_guest_basename(m->guest_path);
+ if (!base || !*base ||
+ !wasm_bytes_eq_cstr(guest_path, (uint32_t)path_len_i, base))
+ return WASI_ENOENT;
+ host_path = (char*)m->host_path;
+ } else {
+ st = wasm_join_path(w->host->config.heap, m->host_path, guest_path,
+ (uint32_t)path_len_i, &host_path, &host_path_size);
+ if (st != KIT_OK) return wasm_status_errno(st);
+ }
+ memset(&data, 0, sizeof data);
+ st = w->host->config.file_io->read_all(w->host->config.file_io->user,
+ host_path, &data);
+ if (!(m->flags & KIT_WASM_FS_FILE))
+ w->host->config.heap->free(w->host->config.heap, host_path,
+ host_path_size);
+ if (st != KIT_OK) return wasm_status_errno(st);
+ if (!wasm_find_free_fd(w, &fd)) {
+ if (w->host->config.file_io->release)
+ w->host->config.file_io->release(w->host->config.file_io->user, &data);
+ return WASI_ENOMEM;
+ }
+ w->fds[fd].used = 1;
+ w->fds[fd].data = data;
+ w->fds[fd].io = w->host->config.file_io;
+ w->fds[fd].pos = 0;
+ if (!wasm_write_u32(inst, (uint32_t)opened_fd_i, fd)) return WASI_EINVAL;
+ return WASI_ESUCCESS;
+}
+
+static int32_t wasi_fd_read(KitWasmInstance* inst, int32_t fd_i,
+ int32_t iovs_i, int32_t iovs_len_i,
+ int32_t nread_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint32_t fd = (uint32_t)fd_i;
+ uint32_t iovs = (uint32_t)iovs_i;
+ uint32_t iovs_len = (uint32_t)iovs_len_i;
+ uint32_t total = 0;
+ uint32_t i;
+ if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF;
+ for (i = 0; i < iovs_len; ++i) {
+ uint32_t ptr;
+ uint32_t len;
+ uint8_t* dst;
+ uint32_t off;
+ uint32_t rec;
+ uint64_t remain64;
+ uint32_t ncopy;
+ if (i > UINT32_MAX / 8u) return WASI_EINVAL;
+ off = i * 8u;
+ if (iovs > UINT32_MAX - off) return WASI_EINVAL;
+ rec = iovs + off;
+ if (!wasm_read_u32(inst, rec, &ptr) ||
+ !wasm_read_u32(inst, rec + 4u, &len) ||
+ !wasm_mem_ptr(inst, ptr, len, &dst))
+ return WASI_EINVAL;
+ if (w->fds[fd].pos >= (uint64_t)w->fds[fd].data.size) break;
+ remain64 = (uint64_t)w->fds[fd].data.size - w->fds[fd].pos;
+ ncopy = len;
+ if ((uint64_t)ncopy > remain64) ncopy = (uint32_t)remain64;
+ if (ncopy)
+ memcpy(dst, w->fds[fd].data.data + (size_t)w->fds[fd].pos, ncopy);
+ w->fds[fd].pos += ncopy;
+ if (total > UINT32_MAX - ncopy) return WASI_EINVAL;
+ total += ncopy;
+ if (ncopy < len) break;
+ }
+ if (!wasm_write_u32(inst, (uint32_t)nread_i, total)) return WASI_EINVAL;
+ return WASI_ESUCCESS;
+}
+
+static int32_t wasi_fd_close(KitWasmInstance* inst, int32_t fd_i) {
+ KitWasmRuntimeInstance* w = wasm_wrap_from_instance(inst);
+ uint32_t fd = (uint32_t)fd_i;
+ if (fd >= KIT_WASM_HOST_MAX_FDS || !w->fds[fd].used) return WASI_EBADF;
+ if (w->fds[fd].io && w->fds[fd].io->release)
+ w->fds[fd].io->release(w->fds[fd].io->user, &w->fds[fd].data);
+ memset(&w->fds[fd], 0, sizeof w->fds[fd]);
+ return WASI_ESUCCESS;
+}
+
+static void* wasm_wasi_resolve(void* user, const char* module,
+ const char* field,
+ const KitWasmImportType* type) {
+ KitWasmHost* host = (KitWasmHost*)user;
+ KitWasmValType r_i32[1] = {KIT_WASM_VAL_I32};
+ KitWasmValType p_proc_exit[1] = {KIT_WASM_VAL_I32};
+ KitWasmValType p2_i32[2] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32};
+ KitWasmValType p4_i32[4] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I32,
+ KIT_WASM_VAL_I32, KIT_WASM_VAL_I32};
+ KitWasmValType p_clock[3] = {KIT_WASM_VAL_I32, KIT_WASM_VAL_I64,
+ KIT_WASM_VAL_I32};
+ KitWasmValType p_path_open[9] = {
+ KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32,
+ KIT_WASM_VAL_I32, KIT_WASM_VAL_I32, KIT_WASM_VAL_I64,
+ KIT_WASM_VAL_I64, KIT_WASM_VAL_I32, KIT_WASM_VAL_I32};
+ if (!host || !(host->config.flags & KIT_WASM_HOST_WASI_PREVIEW1))
+ return NULL;
+ if (!wasm_streq(module, "wasi_snapshot_preview1")) return NULL;
+ if (wasm_streq(field, "proc_exit") &&
+ wasm_sig(type, p_proc_exit, 1u, NULL, 0u))
+ return (void*)(uintptr_t)wasi_proc_exit;
+ if (wasm_streq(field, "fd_write") &&
+ wasm_sig(type, p4_i32, 4u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_fd_write;
+ if (wasm_streq(field, "environ_sizes_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_environ_sizes_get;
+ if (wasm_streq(field, "environ_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_environ_get;
+ if (wasm_streq(field, "args_sizes_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_args_sizes_get;
+ if (wasm_streq(field, "args_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_args_get;
+ if (wasm_streq(field, "random_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_random_get;
+ if (wasm_streq(field, "clock_time_get") &&
+ wasm_sig(type, p_clock, 3u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_clock_time_get;
+ if (wasm_streq(field, "fd_prestat_get") &&
+ wasm_sig(type, p2_i32, 2u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_fd_prestat_get;
+ if (wasm_streq(field, "fd_prestat_dir_name") &&
+ wasm_sig(type, p4_i32, 4u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_fd_prestat_dir_name;
+ if (wasm_streq(field, "path_open") &&
+ wasm_sig(type, p_path_open, 9u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_path_open;
+ if (wasm_streq(field, "fd_read") &&
+ wasm_sig(type, p4_i32, 4u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_fd_read;
+ if (wasm_streq(field, "fd_close") &&
+ wasm_sig(type, p_proc_exit, 1u, r_i32, 1u))
+ return (void*)(uintptr_t)wasi_fd_close;
+ return NULL;
+}
+
+KIT_API KitStatus kit_wasm_host_new(const KitWasmHostConfig* config,
+ KitWasmHost** out) {
+ KitWasmHost* host;
+ uint32_t i;
+ if (!out || !config || !config->heap) return KIT_INVALID;
+ *out = NULL;
+ if (config->nmounts && !config->mounts) return KIT_INVALID;
+ if (config->nmounts > KIT_WASM_HOST_MAX_FDS - 3u) return KIT_INVALID;
+ for (i = 0; i < config->nmounts; ++i) {
+ const KitWasmFsMount* m = &config->mounts[i];
+ if (!m->host_path || !m->guest_path || m->guest_path[0] != '/')
+ return KIT_INVALID;
+ if ((m->flags & KIT_WASM_FS_FILE) &&
+ (!*wasm_guest_basename(m->guest_path)))
+ return KIT_INVALID;
+ }
+ host = (KitWasmHost*)config->heap->alloc(config->heap, sizeof(*host),
+ _Alignof(KitWasmHost));
+ if (!host) return KIT_NOMEM;
+ host->config = *config;
+ *out = host;
+ return KIT_OK;
+}
+
+KIT_API void kit_wasm_host_free(KitWasmHost* host) {
+ KitHeap* heap;
+ if (!host) return;
+ heap = host->config.heap;
+ heap->free(heap, host, sizeof(*host));
+}
+
+KIT_API KitStatus kit_wasm_instance_new(KitWasmHost* host, KitJit* jit,
+ KitWasmInstance** out) {
+ KitWasmRuntimeLayout layout;
+ KitWasmRuntimeInstance* w;
+ KitHeap* heap;
+ uint64_t instance_bytes;
+ uint64_t allocation_bytes;
+ size_t allocation_size;
+ uint64_t total_memory = 0;
+ KitStatus st;
+ uint32_t i;
+ if (!out || !host || !host->config.heap || !jit) return KIT_INVALID;
+ *out = NULL;
+ heap = host->config.heap;
+ st = kit_wasm_get_runtime_layout(jit, &layout);
+ if (st != KIT_OK) return st;
+ if (layout.nmemories > host->config.max_memories) return KIT_INVALID;
+ instance_bytes = layout.instance_size ? layout.instance_size : 1u;
+ if (instance_bytes > host->config.max_instance_bytes) return KIT_INVALID;
+ if (instance_bytes > UINT64_MAX - offsetof(KitWasmRuntimeInstance, instance))
+ return KIT_INVALID;
+ allocation_bytes =
+ (uint64_t)offsetof(KitWasmRuntimeInstance, instance) + instance_bytes;
+ st = wasm_u64_to_size(allocation_bytes, &allocation_size);
+ if (st != KIT_OK) return st;
+ w = (KitWasmRuntimeInstance*)heap->alloc(heap, allocation_size,
+ _Alignof(KitWasmRuntimeInstance));
+ if (!w) return KIT_NOMEM;
+ memset(w, 0, allocation_size);
+ w->host = host;
+ w->memory_layouts = layout.memories;
+ w->allocation_bytes = allocation_bytes;
+ w->instance_bytes = instance_bytes;
+ w->nmemories = layout.nmemories;
+ if (layout.nmemories) {
+ size_t ptr_bytes;
+ size_t bytes_bytes;
+ if ((uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint8_t*) ||
+ (uint64_t)layout.nmemories > (uint64_t)SIZE_MAX / sizeof(uint64_t)) {
+ heap->free(heap, w, allocation_size);
+ return KIT_INVALID;
+ }
+ ptr_bytes = (size_t)layout.nmemories * sizeof(uint8_t*);
+ bytes_bytes = (size_t)layout.nmemories * sizeof(uint64_t);
+ w->memories = (uint8_t**)heap->alloc(heap, ptr_bytes, _Alignof(uint8_t*));
+ w->memory_bytes =
+ (uint64_t*)heap->alloc(heap, bytes_bytes, _Alignof(uint64_t));
+ if (!w->memories || !w->memory_bytes) {
+ if (w->memory_bytes) heap->free(heap, w->memory_bytes, bytes_bytes);
+ if (w->memories) heap->free(heap, w->memories, ptr_bytes);
+ heap->free(heap, w, allocation_size);
+ return KIT_NOMEM;
+ }
+ memset(w->memories, 0, ptr_bytes);
+ memset(w->memory_bytes, 0, bytes_bytes);
+ }
+ for (i = 0; i < layout.nmemories; ++i) {
+ const KitWasmMemoryLayout* ml = &layout.memories[i];
+ KitWasmMemoryRecord* rec;
+ uint64_t mem_bytes;
+ size_t mem_size;
+ if (ml->max_pages < ml->min_pages) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return KIT_MALFORMED;
+ }
+ if (ml->offset > instance_bytes ||
+ instance_bytes - ml->offset < sizeof(KitWasmMemoryRecord)) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return KIT_MALFORMED;
+ }
+ st = wasm_page_bytes(ml->max_pages, &mem_bytes);
+ if (st != KIT_OK) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return st;
+ }
+ if (mem_bytes > host->config.max_total_memory_bytes ||
+ total_memory > host->config.max_total_memory_bytes - mem_bytes) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return KIT_INVALID;
+ }
+ total_memory += mem_bytes;
+ st = wasm_u64_to_size(mem_bytes, &mem_size);
+ if (st != KIT_OK) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return st;
+ }
+ if (mem_size) {
+ w->memories[i] = (uint8_t*)heap->alloc(heap, mem_size, 1u);
+ if (!w->memories[i]) {
+ kit_wasm_instance_free(wasm_instance_from_wrap(w));
+ return KIT_NOMEM;
+ }
+ memset(w->memories[i], 0, mem_size);
+ }
+ w->memory_bytes[i] = mem_bytes;
+ rec = (KitWasmMemoryRecord*)(w->instance + ml->offset);
+ rec->data = w->memories[i];
+ }
+ w->total_memory_bytes = total_memory;
+ *out = wasm_instance_from_wrap(w);
+ return KIT_OK;
+}
+
+KIT_API void kit_wasm_instance_free(KitWasmInstance* inst) {
+ KitWasmRuntimeInstance* w;
+ KitHeap* heap;
+ uint32_t i;
+ size_t allocation_size;
+ if (!inst) return;
+ w = wasm_wrap_from_instance(inst);
+ heap = w->host->config.heap;
+ for (i = 0; i < KIT_WASM_HOST_MAX_FDS; ++i) {
+ if (w->fds[i].used && w->fds[i].io && w->fds[i].io->release)
+ w->fds[i].io->release(w->fds[i].io->user, &w->fds[i].data);
+ }
+ for (i = 0; i < w->nmemories; ++i) {
+ size_t mem_size;
+ if (w->memories && w->memories[i] &&
+ wasm_u64_to_size(w->memory_bytes[i], &mem_size) == KIT_OK)
+ heap->free(heap, w->memories[i], mem_size);
+ }
+ if (w->memories)
+ heap->free(heap, w->memories, (size_t)w->nmemories * sizeof(uint8_t*));
+ if (w->memory_bytes)
+ heap->free(heap, w->memory_bytes,
+ (size_t)w->nmemories * sizeof(uint64_t));
+ allocation_size = (size_t)w->allocation_bytes;
+ heap->free(heap, w, allocation_size);
+}
+
+KIT_API KitStatus kit_wasm_host_bind_imports(KitWasmHost* host,
+ KitCompiler* compiler,
+ KitJit* jit,
+ KitWasmInstance* inst) {
+ if (!host || !compiler || !jit || !inst) return KIT_INVALID;
+ return kit_wasm_bind_host_imports(compiler, jit, inst, NULL, 0,
+ wasm_wasi_resolve, host);
+}
+
+KIT_API int kit_wasm_instance_exit_code(KitWasmInstance* inst, int* code_out) {
+ KitWasmRuntimeInstance* w;
+ if (!inst) return 0;
+ w = wasm_wrap_from_instance(inst);
+ if (!w->exit_called) return 0;
+ if (code_out) *code_out = w->exit_code;
+ return 1;
+}
diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c
@@ -2525,20 +2525,28 @@ static const ABIArgInfo* aa_param_abi(NativeTarget* t, const ABIFuncInfo* abi,
return scratch;
}
-/* Stack footprint of a single argument part: each occupies a multiple of 8
- * bytes (so a 16-byte FP part such as binary128 takes 16, not 8). */
-static u32 aa_part_stack_size(const ABIArgPart* part) {
- return align_up_u32(part->size ? part->size : 8u, 8u);
+/* Stack footprint of a single argument part. AAPCS64 uses 8-byte slots;
+ * Apple ARM64 uses compact 4-byte slots for stack-passed int32-sized values. */
+static u32 aa_stack_arg_min_align(const ABIFuncInfo* abi) {
+ return (abi && abi->stack_arg_min_align) ? abi->stack_arg_min_align : 8u;
}
-/* Natural stack alignment of a part: at least 8, capped at 16 (binary128). */
-static u32 aa_part_stack_align(const ABIArgPart* part) {
+/* Natural stack alignment of a part, capped at 16 (binary128). */
+static u32 aa_part_stack_align(const ABIFuncInfo* abi,
+ const ABIArgPart* part) {
+ u32 min_align = aa_stack_arg_min_align(abi);
u32 al = part->align ? part->align : 8u;
- if (al < 8u) al = 8u;
+ if (al < min_align) al = min_align;
if (al > 16u) al = 16u;
return al;
}
+static u32 aa_part_stack_size(const ABIFuncInfo* abi,
+ const ABIArgPart* part) {
+ return align_up_u32(part->size ? part->size : 8u,
+ aa_part_stack_align(abi, part));
+}
+
/* The scalar type used to move one ABI part through a register. Aggregate
* args/results are split into parts; each part must move at its own width, not
* the (possibly >8-byte) aggregate width. */
@@ -2560,15 +2568,16 @@ static KitCgTypeId aa_part_scalar_type(const ABIArgPart* part) {
}
}
-static u32 aa_class_stack_size(const ABIArgInfo* ai) {
+static u32 aa_class_stack_size(const ABIFuncInfo* abi, const ABIArgInfo* ai) {
u32 total = 0;
if (!ai || ai->kind == ABI_ARG_IGNORE) return 0;
if (ai->kind == ABI_ARG_INDIRECT) return 8u;
for (u32 p = 0; p < ai->nparts; ++p) {
- total = align_up_u32(total, aa_part_stack_align(&ai->parts[p]));
- total += aa_part_stack_size(&ai->parts[p]);
+ total = align_up_u32(total, aa_part_stack_align(abi, &ai->parts[p]));
+ total += aa_part_stack_size(abi, &ai->parts[p]);
}
- return align_up_u32(total ? total : 8u, 8u);
+ return align_up_u32(total ? total : aa_stack_arg_min_align(abi),
+ aa_stack_arg_min_align(abi));
}
static u32 aa_call_stack_size(NativeTarget* t, const NativeCallDesc* desc) {
@@ -2581,7 +2590,7 @@ static u32 aa_call_stack_size(NativeTarget* t, const NativeCallDesc* desc) {
abi && abi->variadic && abi->vararg_on_stack && i >= abi->nparams;
if (ai->kind == ABI_ARG_IGNORE) continue;
if (force_stack) {
- stack += aa_class_stack_size(ai);
+ stack += aa_class_stack_size(abi, ai);
continue;
}
if (ai->kind == ABI_ARG_INDIRECT) {
@@ -2597,15 +2606,15 @@ static u32 aa_call_stack_size(NativeTarget* t, const NativeCallDesc* desc) {
if (next_fp < 8u)
next_fp++;
else {
- stack = align_up_u32(stack, aa_part_stack_align(part));
- stack += aa_part_stack_size(part);
+ stack = align_up_u32(stack, aa_part_stack_align(abi, part));
+ stack += aa_part_stack_size(abi, part);
}
} else {
if (next_int < 8u)
next_int++;
else {
- stack = align_up_u32(stack, aa_part_stack_align(part));
- stack += aa_part_stack_size(part);
+ stack = align_up_u32(stack, aa_part_stack_align(abi, part));
+ stack += aa_part_stack_size(abi, part);
}
}
}
@@ -2723,12 +2732,13 @@ static void aa_plan_call(NativeTarget* t, const NativeCallDesc* desc,
if (force_stack) {
NativeLoc tmpreg =
native_loc_reg(desc->args[i].type, NATIVE_REG_INT, AA_TMP0);
- u32 n = aa_class_stack_size(ai);
+ u32 n = aa_class_stack_size(abi, ai);
u32 off = 0;
while (off < n) {
- aa_load_part(t, tmpreg, desc->args[i], off, 8);
- aa_store_outgoing_part(t, tail_call, stack + off, tmpreg, 8);
- off += 8;
+ u32 chunk = (n - off > 8u) ? 8u : (n - off);
+ aa_load_part(t, tmpreg, desc->args[i], off, chunk);
+ aa_store_outgoing_part(t, tail_call, stack + off, tmpreg, chunk);
+ off += chunk;
}
stack += n;
continue;
@@ -2769,9 +2779,9 @@ static void aa_plan_call(NativeTarget* t, const NativeCallDesc* desc,
NativeLoc tmpreg = native_loc_reg(
desc->args[i].type, cls, cls == NATIVE_REG_FP ? 16u : AA_TMP0);
aa_load_part(t, tmpreg, desc->args[i], part->src_offset, part->size);
- stack = align_up_u32(stack, aa_part_stack_align(part));
+ stack = align_up_u32(stack, aa_part_stack_align(abi, part));
aa_store_outgoing_part(t, tail_call, stack, tmpreg, part->size);
- stack += aa_part_stack_size(part);
+ stack += aa_part_stack_size(abi, part);
}
}
}
@@ -3867,14 +3877,14 @@ static void aa_bind_native_param(NativeTarget* t, const CGParamDesc* p,
NativeAddr saddr;
src = native_loc_reg(p->type, cls, tmp);
a->next_param_stack =
- align_up_u32(a->next_param_stack, aa_part_stack_align(part));
+ align_up_u32(a->next_param_stack, aa_part_stack_align(abi, part));
memset(&saddr, 0, sizeof saddr);
saddr.base_kind = NATIVE_ADDR_BASE_REG;
saddr.base.reg = AA_FP;
saddr.base_type = p->type;
saddr.offset = aa_fp_off_in_arg(a, a->next_param_stack);
aa_emit_mem(a, 1, src, saddr, aa_mem_for_type(t, p->type, part->size));
- a->next_param_stack += aa_part_stack_size(part);
+ a->next_param_stack += aa_part_stack_size(abi, part);
}
if (dst.kind == NATIVE_LOC_NONE) {
/* Unused parameter: only the ABI cursor advances. */
diff --git a/test/driver-wasm/run.sh b/test/driver-wasm/run.sh
@@ -0,0 +1,325 @@
+#!/bin/sh
+# Driver-level checks for `kit run` Wasm sandbox CLI behavior.
+
+set -u
+
+script_dir=$(cd "$(dirname "$0")" && pwd)
+repo_root=$(cd "$script_dir/../.." && pwd)
+
+KIT="${KIT:-$repo_root/build/kit}"
+
+if [ ! -x "$KIT" ]; then
+ echo "driver-wasm: kit binary not found at $KIT" >&2
+ exit 2
+fi
+
+work=$(mktemp -d "${TMPDIR:-/tmp}/kit-driver-wasm-test.XXXXXX")
+trap 'rm -rf "$work"' EXIT
+
+KIT_KIT_DIR="$repo_root/test/lib"
+. "$repo_root/test/lib/kit_sh_kit.sh"
+kit_report_init
+
+cat > "$work/return0.wat" <<'WAT'
+(module
+ (func (export "test_main") (result i32)
+ i32.const 0))
+WAT
+
+cat > "$work/host_add.wat" <<'WAT'
+(module
+ (import "env" "host_add" (func $host_add (param i32 i32) (result i32)))
+ (func (export "test_main") (result i32)
+ i32.const 21
+ i32.const 21
+ call $host_add
+ i32.const 42
+ i32.sub))
+WAT
+
+cat > "$work/memory_2page_max.wat" <<'WAT'
+(module
+ (memory 1 2)
+ (func (export "test_main") (result i32)
+ i32.const 0))
+WAT
+
+cat > "$work/memory_one.wat" <<'WAT'
+(module
+ (memory 1)
+ (func (export "test_main") (result i32)
+ i32.const 0))
+WAT
+
+cat > "$work/wasi_exit.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32)))
+ (func (export "test_main") (result i32)
+ i32.const 7
+ call $proc_exit
+ i32.const 0))
+WAT
+
+cat > "$work/wasi_stdout.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "fd_write"
+ (func $fd_write (param i32 i32 i32 i32) (result i32)))
+ (memory 1)
+ (data (i32.const 0) "\08\00\00\00\0b\00\00\00")
+ (data (i32.const 8) "hello wasi\0a")
+ (func (export "test_main") (result i32)
+ i32.const 1
+ i32.const 0
+ i32.const 1
+ i32.const 20
+ call $fd_write
+ i32.const 0
+ i32.ne))
+WAT
+
+cat > "$work/wasi_env.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "environ_sizes_get"
+ (func $sizes (param i32 i32) (result i32)))
+ (import "wasi_snapshot_preview1" "environ_get"
+ (func $get (param i32 i32) (result i32)))
+ (memory 1)
+ (func (export "test_main") (result i32)
+ (local $err i32)
+ (local $p i32)
+ i32.const 0
+ i32.const 4
+ call $sizes
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ (if (i32.ne (i32.load (i32.const 0)) (i32.const 1))
+ (then (return (i32.const 10))))
+ i32.const 8
+ i32.const 64
+ call $get
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ i32.const 8
+ i32.load
+ local.set $p
+ (if (i32.ne (i32.load8_u (local.get $p)) (i32.const 75))
+ (then (return (i32.const 11))))
+ (if (i32.ne (i32.load8_u offset=13 (local.get $p)) (i32.const 61))
+ (then (return (i32.const 12))))
+ (if (i32.ne (i32.load8_u offset=14 (local.get $p)) (i32.const 111))
+ (then (return (i32.const 13))))
+ i32.const 0))
+WAT
+
+cat > "$work/wasi_args.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "args_sizes_get"
+ (func $sizes (param i32 i32) (result i32)))
+ (import "wasi_snapshot_preview1" "args_get"
+ (func $get (param i32 i32) (result i32)))
+ (memory 1)
+ (func (export "test_main") (result i32)
+ (local $err i32)
+ (local $p i32)
+ i32.const 0
+ i32.const 4
+ call $sizes
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ (if (i32.ne (i32.load (i32.const 0)) (i32.const 2))
+ (then (return (i32.const 20))))
+ i32.const 8
+ i32.const 64
+ call $get
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ i32.const 12
+ i32.load
+ local.set $p
+ (if (i32.ne (i32.load8_u (local.get $p)) (i32.const 97))
+ (then (return (i32.const 21))))
+ (if (i32.ne (i32.load8_u offset=4 (local.get $p)) (i32.const 97))
+ (then (return (i32.const 22))))
+ i32.const 0))
+WAT
+
+cat > "$work/wasi_fs_read.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "path_open"
+ (func $path_open
+ (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
+ (import "wasi_snapshot_preview1" "fd_read"
+ (func $fd_read (param i32 i32 i32 i32) (result i32)))
+ (import "wasi_snapshot_preview1" "fd_close"
+ (func $fd_close (param i32) (result i32)))
+ (memory 1)
+ (data (i32.const 0) "\80\00\00\00\05\00\00\00")
+ (data (i32.const 64) "hello.txt")
+ (func (export "test_main") (result i32)
+ (local $fd i32)
+ (local $err i32)
+ i32.const 3
+ i32.const 0
+ i32.const 64
+ i32.const 9
+ i32.const 0
+ i64.const 0
+ i64.const 0
+ i32.const 0
+ i32.const 16
+ call $path_open
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ i32.const 16
+ i32.load
+ local.set $fd
+ local.get $fd
+ i32.const 0
+ i32.const 1
+ i32.const 20
+ call $fd_read
+ local.set $err
+ (if (i32.ne (local.get $err) (i32.const 0))
+ (then (return (local.get $err))))
+ (if (i32.ne (i32.load (i32.const 20)) (i32.const 5))
+ (then (return (i32.const 30))))
+ (if (i32.ne (i32.load8_u (i32.const 128)) (i32.const 104))
+ (then (return (i32.const 31))))
+ (if (i32.ne (i32.load8_u offset=4 (i32.const 128)) (i32.const 111))
+ (then (return (i32.const 32))))
+ local.get $fd
+ call $fd_close
+ drop
+ i32.const 0))
+WAT
+
+cat > "$work/wasi_unknown.wat" <<'WAT'
+(module
+ (import "wasi_snapshot_preview1" "path_unlink_file"
+ (func $unlink (param i32 i32 i32) (result i32)))
+ (memory 1)
+ (func (export "test_main") (result i32)
+ i32.const 0
+ i32.const 0
+ i32.const 0
+ call $unlink))
+WAT
+
+cat > "$work/main.c" <<'SRC'
+int main(void) { return 0; }
+SRC
+
+printf 'hello' > "$work/hello.txt"
+
+run_ok "wasm-default-runs-no-imports" \
+ "$KIT" run -e test_main "$work/return0.wat"
+
+KIT_TEST_HOST_IMPORTS=1 "$KIT" run -e test_main "$work/host_add.wat" \
+ > "$work/default-deny.out" 2> "$work/default-deny.err"
+if [ "$?" -ne 0 ]; then
+ ok "wasm-default-denies-test-env-fallback"
+else
+ not_ok "wasm-default-denies-test-env-fallback" "$work/default-deny.out"
+fi
+contains "wasm-default-deny-diag" "$work/default-deny.err" \
+ "wasm host import unresolved"
+
+run_ok "wasm-explicit-test-import" \
+ "$KIT" run --wasm-imports=test -e test_main "$work/host_add.wat"
+
+run_fail "wasm-memory-cap-fails" \
+ "$KIT" run --wasm-memory-max=64K -e test_main "$work/memory_2page_max.wat"
+contains "wasm-memory-cap-diag" "$work/wasm-memory-cap-fails.err" \
+ "wasm linear memory reservation exceeds 65536 bytes"
+
+run_ok "wasm-memory-cap-allows" \
+ "$KIT" run --wasm-memory-max=128K -e test_main "$work/memory_2page_max.wat"
+
+run_fail "wasm-memories-cap-fails" \
+ "$KIT" run --wasm-memories-max=0 -e test_main "$work/memory_one.wat"
+contains "wasm-memories-cap-diag" "$work/wasm-memories-cap-fails.err" \
+ "wasm memory count exceeds 0"
+
+run_ok "wasm-policy-flags-parse" \
+ "$KIT" run --wasm-env=inherit --wasm-env-pass=PATH \
+ --wasm-env-set=KIT_TEST=1 --wasm-map-dir="$work=/work:ro" \
+ --wasm-map-file="$work/return0.wat=/return0.wat:ro" --wasm-cwd=/work \
+ --wasm-stdio=inherit --wasm-clock=monotonic --wasm-random=seed:abcd \
+ -e test_main "$work/return0.wat"
+
+"$KIT" run --wasm-wasi -e test_main "$work/wasi_exit.wat" \
+ > "$work/wasm-wasi-proc-exit.out" 2> "$work/wasm-wasi-proc-exit.err"
+if [ "$?" -eq 7 ]; then
+ ok "wasm-wasi-proc-exit"
+else
+ not_ok "wasm-wasi-proc-exit" "$work/wasm-wasi-proc-exit.err"
+fi
+
+"$KIT" run --wasm-wasi --wasm-stdio=inherit -e test_main \
+ "$work/wasi_stdout.wat" > "$work/wasm-wasi-stdout.out" \
+ 2> "$work/wasm-wasi-stdout.err"
+if [ "$?" -eq 0 ]; then ok "wasm-wasi-stdout"; else
+ not_ok "wasm-wasi-stdout" "$work/wasm-wasi-stdout.err"
+fi
+contains "wasm-wasi-stdout-text" "$work/wasm-wasi-stdout.out" "hello wasi"
+
+"$KIT" run --wasm-wasi --wasm-stdio=null -e test_main \
+ "$work/wasi_stdout.wat" > "$work/wasm-wasi-stdout-null.out" \
+ 2> "$work/wasm-wasi-stdout-null.err"
+if [ "$?" -eq 0 ] && [ ! -s "$work/wasm-wasi-stdout-null.out" ]; then
+ ok "wasm-wasi-stdout-null"
+else
+ not_ok "wasm-wasi-stdout-null" "$work/wasm-wasi-stdout-null.err"
+fi
+
+run_ok "wasm-wasi-env-set" \
+ "$KIT" run --wasm-wasi --wasm-env-set=KIT_WASM_TEST=ok \
+ -e test_main "$work/wasi_env.wat"
+
+run_ok "wasm-wasi-args" \
+ "$KIT" run --wasm-wasi -e test_main "$work/wasi_args.wat" -- alpha
+
+run_ok "wasm-wasi-fs-read" \
+ "$KIT" run --wasm-wasi --wasm-map-dir="$work=/sandbox:ro" \
+ -e test_main "$work/wasi_fs_read.wat"
+
+run_ok "wasm-wasi-fs-map-file-read" \
+ "$KIT" run --wasm-wasi \
+ --wasm-map-file="$work/hello.txt=/sandbox/hello.txt:ro" \
+ -e test_main "$work/wasi_fs_read.wat"
+
+run_fail "wasm-wasi-unknown-import-fails" \
+ "$KIT" run --wasm-wasi -e test_main "$work/wasi_unknown.wat"
+contains "wasm-wasi-unknown-import-diag" \
+ "$work/wasm-wasi-unknown-import-fails.err" \
+ "wasm host import unresolved"
+
+run_fail "wasm-flags-reject-native" \
+ "$KIT" run --wasm-memory-max=1M "$work/main.c"
+contains "wasm-flags-reject-native-diag" "$work/wasm-flags-reject-native.err" \
+ "wasm sandbox flags require a wasm input"
+
+run_fail "wasm-rejects-mixed-native-inputs" \
+ "$KIT" run -e test_main "$work/return0.wat" "$work/main.c"
+contains "wasm-rejects-mixed-native-inputs-diag" \
+ "$work/wasm-rejects-mixed-native-inputs.err" \
+ "sandboxed wasm run does not support mixed native inputs"
+
+run_fail "wasm-import-mode-invalid" \
+ "$KIT" run --wasm-imports=env -e test_main "$work/return0.wat"
+contains "wasm-import-mode-invalid-diag" \
+ "$work/wasm-import-mode-invalid.err" \
+ "unknown --wasm-imports value"
+
+run_fail "wasm-fs-invalid" \
+ "$KIT" run --wasm-fs=host -e test_main "$work/return0.wat"
+contains "wasm-fs-invalid-diag" "$work/wasm-fs-invalid.err" \
+ "unknown --wasm-fs value"
+
+kit_summary test-driver-wasm
+kit_exit
diff --git a/test/wasm/run.sh b/test/wasm/run.sh
@@ -69,7 +69,7 @@ case "$TEST_OBJ" in
esac
EXEC_TAG="$EXEC_ARCH-$EXEC_OS"
export KIT_TEST_ARCH="$TEST_ARCH" KIT_TEST_OBJ="$TEST_OBJ"
-export KIT_TEST_HOST_IMPORTS=1 # enable the runner's canned host-import resolver
+WASM_RUN_TEST_IMPORTS=(--wasm-imports=test)
have_wasm_tool=0; [ -x "$WASM_TOOL" ] && have_wasm_tool=1
have_jit_runner=0; [ -x "$JIT_RUNNER" ] && [ "$host_matches" -eq 1 ] && have_jit_runner=1
@@ -221,13 +221,13 @@ kit_lane_W() {
else kit_skip "$KIT_NAME/W" "no wasm-tool"; fi
}
kit_lane_D() {
- wf_rc "$KIT_NAME/D-wat" "$KIT_EXPECTED" "$KIT_BIN" run -e test_main "$KIT_SRC"
- if wasm_build; then wf_rc "$KIT_NAME/D-wasm" "$KIT_EXPECTED" "$KIT_BIN" run -e test_main "$WASM"
+ wf_rc "$KIT_NAME/D-wat" "$KIT_EXPECTED" "$KIT_BIN" run "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$KIT_SRC"
+ if wasm_build; then wf_rc "$KIT_NAME/D-wasm" "$KIT_EXPECTED" "$KIT_BIN" run "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$WASM"
else kit_skip "$KIT_NAME/D-wasm" "no wasm-tool"; fi
}
kit_lane_N() {
- wf_rc_interp "$KIT_NAME/N-wat" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit -e test_main "$KIT_SRC"
- if wasm_build; then wf_rc_interp "$KIT_NAME/N-wasm" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit -e test_main "$WASM"
+ wf_rc_interp "$KIT_NAME/N-wat" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$KIT_SRC"
+ if wasm_build; then wf_rc_interp "$KIT_NAME/N-wasm" "$KIT_EXPECTED" "$KIT_BIN" run --no-jit "${WASM_RUN_TEST_IMPORTS[@]}" -e test_main "$WASM"
else kit_skip "$KIT_NAME/N-wasm" "no wasm-tool"; fi
}
kit_lane_O() {