Skip to main content

WASI in Exec Mode

Wasmrun's exec mode provides WASI Preview 1 support, enabling WASM modules to interact with the host system through a controlled syscall interface.

Supported Syscalls

SyscallDescriptionStatus
fd_writeWrite to file descriptors (stdout, stderr): reads iovecs from memory
fd_readRead from file descriptors (stdin returns EOF)
fd_closeClose a file descriptor
fd_seekSeek within a file descriptor
fd_fdstat_getFile descriptor status (filetype, flags, rights)
fd_prestat_getPreopened directory info (returns the preopen, or EBADF when none mounted)
fd_prestat_dir_namePreopened directory name
args_getRetrieve command-line arguments from memory
args_sizes_getGet argument count and buffer sizes
environ_getRetrieve environment variables from memory
environ_sizes_getGet environment variable count and sizes
clock_time_getGet current time (realtime, monotonic) in nanoseconds
random_getFill buffer with random bytes
proc_exitExit with a status code (terminates execution cleanly)
poll_oneoffPoll for events (stub, returns ENOSYS)✅ Stub
sched_yieldYield execution (stub, returns success)✅ Stub
path_openOpen (or create) a file by path
path_filestat_getStat a path
path_create_directoryCreate a directory
path_remove_directoryRemove a directory
path_unlink_fileDelete a file
path_renameRename / move a path
fd_readdirRead directory entries
fd_filestat_getStat an open file descriptor
fd_fdstat_set_flagsSet file descriptor flags
path_filestat_set_timesSet path timestamps (returns ENOSYS)✅ Stub
path_readlinkRead a symlink target (returns ENOSYS)✅ Stub
path_symlinkCreate a symlink (returns ENOSYS)✅ Stub

How It Works

WASI syscalls are registered as host functions in the linker under the wasi_snapshot_preview1 module namespace. When the WASM module calls an imported function, the executor dispatches to the corresponding Rust implementation with access to linear memory.

WASM module calls fd_write(fd=1, iovs, iovs_len, nwritten)
→ executor detects imported function (func_idx < import_count)
→ dispatches to host function via linker
→ host reads iovec structs {buf_ptr, buf_len} from linear memory
→ reads string bytes from memory at buf_ptr
→ appends bytes to WasiEnv stdout buffer
→ writes total bytes_written to nwritten pointer in memory
→ returns errno (0 = success)

Environment Setup

The WasiEnv struct configures the WASI environment:

WasiEnv::new()
.with_args(vec!["program".into(), "arg1".into()])
.with_env("KEY".into(), "value".into())
  • Arguments: written to linear memory via args_get / args_sizes_get
  • Environment variables: written as KEY=VALUE\0 strings via environ_get / environ_sizes_get
  • Output capture: stdout/stderr buffered in WasiEnv for programmatic access via get_stdout() / get_stderr()

Linker Integration

The executor uses a Linker to resolve imported functions. The linker maps (module, name) pairs to host function implementations:

let wasi_env = Arc::new(Mutex::new(WasiEnv::new()));
let linker = create_wasi_linker(wasi_env.clone());
let mut executor = Executor::new_with_linker(module, linker)?;

Host functions receive &mut LinearMemory so they can read pointers and write results directly into the module's address space.

Clock Support

Clock IDConstantDescription
REALTIME0Wall clock time (nanoseconds since Unix epoch)
MONOTONIC1Monotonically increasing (for measuring intervals)

Process Exit

proc_exit terminates execution by raising a sentinel error that the executor catches. The exit code is extracted and returned to the caller:

match executor.execute_with_args(func_idx, args) {
Ok(_) => 0,
Err(e) => Executor::is_proc_exit(&e).unwrap_or(-1),
}

Filesystem

Exec mode bridges the executor to wasmrun's WasiFilesystem, so modules can open, read, write, list, and delete files through the path_* / fd_* syscalls above. A host directory is mounted into the sandbox as a WASI preopen; the agent API, for example, preopens each session's temp directory at /.

  • Preopened directories: host directories mounted to a virtual path, surfaced via fd_prestat_get / fd_prestat_dir_name
  • Path traversal protection: every guest path is resolved and confined to its mount; .. escapes are rejected
  • Read-only mode: when enabled, writes and creates fail instead of mutating the host
  • File size limit: a write larger than the configured per-file cap is rejected with EFBIG
  • Disk quota: in the agent, a write that would push the session's total on-disk footprint past --max-disk is rejected with EDQUOT (see Agent API)