Wasm3WasmRuntime
Kotlin/Native implementation of WasmRuntime backed by the wasm3 C interpreter (Apple targets).
load parses + instantiates a module under the capability sandbox once; the returned Op drives every invocation over the module's linear memory via the warp ABI:
warp_alloc(len: i32) -> i32— guest returns a writable pointer forlenbytes.warp_run(ptr: i32, len: i32) -> i64— guest processesmemory[ptr..ptr+len)and returns a packed pointer/length:(resPtr.toLong() shl 32) or (resLen.toLong() and 0xFFFF_FFFF).
Sandbox guards — mirrors ChicoryWasmRuntime semantics.
Load-time (fully implemented):
Import rejection — wasm3 links imports lazily (a missing import only errors when the importing function is compiled/called), so this cannot rely on
m3_LoadModulefailing. The parsed module's declared shape is inspected up front: any imported function, imported global, or imported memory is a capability violation → WasmLoadException.Memory ceiling — a declared initial or maximum linear-memory page count exceeding WasmSandboxConfig.maxMemoryPages → WasmLoadException. A module declaring memory with no explicit max is also rejected: wasm3 defaults the runtime ceiling to 65536 pages (~4 GiB) for such a module, so its kernel could
memory.growunbounded — a memory-bomb DoS. Requiring a boundedmax <= capcloses it; wasm3 enforces the declared max at grow time. A module declaring no linear memory at all is rejected too — the warp ABI requires memory to marshal args/results.Malformed bytes —
m3_ParseModulefailure → WasmLoadException.Missing ABI export —
m3_FindFunctionfailure forwarp_alloc/warp_run→ WasmLoadException. This is terminal, not a transient error: a verified-but-broken kernel that escapedloadas a raw error would bypass the executor's terminal-error handling and trigger an anti-entropy retry storm on every peer (a remotely-triggerable DoS).
Run-time: any M3Result error from warp_alloc/warp_run/the result read — a trap, unreachable, out-of-bounds access, or a bad packed result — surfaces as WasmExecutionException.
Execution timeout — the CPU-bomb defense. The vendored wasm3 interpreter is patched (grep WARP PATCH under src/nativeInterop/wasm3/source/) to poll its m3_Yield hook on every loop backward branch in addition to upstream's every-function-call-entry poll — the two sites an unbounded computation must pass through, mirroring where ChicoryWasmRuntime's interpreter checks Thread.isInterrupted(). warp_deadline.c supplies the strong m3_Yield: a thread-local wall-clock deadline armed around each ABI round trip (WasmSandboxConfig.executionTimeout) and cleared after, trapping a runaway guest cooperatively on its own thread — no cross-thread abort, no abandoned worker. The deadline trap surfaces as WasmExecutionException naming the exceeded budget. The deadline is armed around every guest execution, including load: wasm3 runs a module's (start) function lazily inside the first m3_FindFunction — the ABI-export lookup in load — so that lookup runs under the armed deadline too, and a CPU-bomb (start) fails as a terminal WasmLoadException naming the exceeded budget instead of hanging load().
Thread-safety. wasm3 is not thread-safe: the shared environment is guarded by loadLock across load, and each Op's runtime is guarded by its own lock across an invocation. Both are real reentrantLocks, not dispatcher confinement. The execution deadline is thread-local in the C shim and armed/cleared inside the Op's lock on the invoking thread, so concurrent runtimes never see each other's budgets.
Parameters
Sandbox configuration (memory cap, execution timeout).