wasi

What is WASI?

WASI stands for WebAssembly System Interface. It's like a bridge between WebAssembly (a type of code that runs in web browsers) and the operating system (like Windows or macOS). WASI lets WebAssembly programs access the operating system's features, like reading and writing files.

Why is WASI important?

WASI makes it possible to run WebAssembly programs outside of web browsers. This means that WebAssembly can be used to develop a wider range of applications, such as desktop applications, server-side applications, and embedded systems.

How do you use WASI?

To use WASI, you need to create a WebAssembly program and then run it with a WASI runtime. A WASI runtime is a program that provides the operating system features that the WebAssembly program needs.

Here's an example of a simple WebAssembly program that uses WASI to read a file:

(module
  (import "wasi_snapshot_preview1" "fd_read" (func $fd_read (param i32 i32 i32 i32) (result i32)))

  (memory 1)
  (export "memory" (memory 0))

  (data (i32.const 8) "hello world")

  (func $main (export "_start")
    (call $fd_read
      (i32.const 0) ;; file_descriptor - 0 for stdin
      (i32.const 8) ;; iovs - The pointer to the iov array, which is stored at memory location 8
      (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one.
      (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written
    )
    drop ;; Discard the number of bytes written from the top of the stack
  )
)

To run this program, you need to compile it to WebAssembly and then run it with a WASI runtime. Here's an example of how to do this using the wabt tool:

$ wat2wasm hello_wasi.wat
$ wasmtime hello_wasi.wasm

This will print the following output:

hello world

Potential applications of WASI

WASI has a wide range of potential applications, including:

  • Desktop applications: WASI can be used to develop desktop applications that run on multiple operating systems.

  • Server-side applications: WASI can be used to develop server-side applications that are more secure and efficient than traditional applications.

  • Embedded systems: WASI can be used to develop embedded systems that are more portable and secure.


Security in WASI for Node.js

What is WASI?

WASI (WebAssembly System Interface) is a set of standards that allow WebAssembly programs to interact with the operating system. It provides a sandboxed environment for programs to run in, so that they can't access or modify parts of the system that they shouldn't.

Capabilities-Based Model

WASI uses a capabilities-based model to control what programs can do. This means that programs are only given the permissions that they need to perform their tasks. For example, a program that needs to read and write files would be given the "file system" capability.

Node.js Threat Model

Currently, Node.js's threat model does not provide the same level of security as some other WASI runtimes. This means that it's possible for programs to escape the sandbox and do things that they shouldn't.

Future Plans

The Node.js project is exploring ways to improve the security of its WASI runtime. This could involve adding features like file system sandboxing and memory protection.

Real-World Applications

WASI can be used in a variety of real-world applications, including:

  • Secure web hosting: WASI can be used to create secure containers for web applications. This can help to protect against attacks such as cross-site scripting and injection vulnerabilities.

  • Cloud computing: WASI can be used to create secure functions for cloud computing platforms. This can help to reduce the risk of data breaches and other security incidents.

  • Internet of Things (IoT): WASI can be used to create secure firmware for IoT devices. This can help to protect these devices from being hacked and used for malicious purposes.

Complete Code Example

The following code example shows how to use the WASI capabilities-based model in Node.js:

const wasi = require("wasi");

// Create a new WASI instance
const instance = new wasi.WASI();

// Add the file system capability
instance.addCapability("wasi_snapshot_preview1:fd_prestat");

// Start the WASI instance
instance.start();

// Use the file system capability to open a file
const fd = instance.fs.open("myfile.txt", wasi.constants.O_RDONLY);

// Read the file
const data = instance.fs.read(fd);

// Close the file
instance.fs.close(fd);

// Stop the WASI instance
instance.stop();

This code example shows how to use the file system capability to open and read a file. The wasi_snapshot_preview1:fd_prestat capability is required to use the fs module.


Class: WASI

The WASI class in Node.js represents a WebAssembly System Interface (WASI) environment, a set of system calls that allow WebAssembly applications to interact with the host system.

Key Features:

  • System Call API: Provides access to the WASI system calls, allowing applications to perform operations such as file I/O, networking, and process management.

  • Convenience Methods: Includes additional methods for simplifying common tasks, such as reading and writing files, and working with memory.

  • Environment Isolation: Each WASI instance represents a separate environment, ensuring isolation and security between applications.

Usage:

Create a new WASI instance to create a sandboxed environment for a WebAssembly application:

const wasi = new WASI();

Use the system call API to perform operations:

wasi.fd_prestat_get(1, (err, stats) => { ... });

Use convenience methods for common tasks:

wasi.writeSync(3, "Hello, WASI!");

Real-World Applications:

  • WebAssembly Sandboxing: Isolating WebAssembly applications from the host system to prevent malicious code from harming the system.

  • Secure Enclaves: Creating secure environments for executing sensitive code or data, such as financial transactions or healthcare records.

  • Microservices: Packaging WebAssembly applications into self-contained environments for easy deployment and management.

Improved Code Example:

const fs = require("fs");

// Create a new WASI instance
const wasi = new WASI();

// Open a file for reading
const fd = await wasi.fd_open("input.txt", "r");

// Read the file's contents
const data = await wasi.fd_read(fd, 1024);

// Close the file
await wasi.fd_close(fd);

// Print the file's contents
console.log(data.toString());

Code Implementation:

// Import the WASI module
const { WASI } = require("wasi");

// Create a WASI instance
const wasi = new WASI();

// Create a virtual file system
const fs = require("memfs").create();

// Add a file to the virtual file system
fs.writeFileSync("/input.txt", "Hello, WASI!");

// Mount the virtual file system in the WASI instance
wasi.mount("/input.txt", fs);

// Create a WebAssembly instance and execute it in the WASI environment
const wasmInstance = await WebAssembly.instantiate(wasmModule, {
  wasi_snapshot_preview1: wasi,
});

// Call a function in the WebAssembly module
const result = wasmInstance.exports.main();

// Print the result
console.log(result);

Overview

WASI (WebAssembly System Interface) provides a portable system interface for WebAssembly applications, allowing them to interact with the underlying operating system in a standardized way.

Options

When creating a WASI instance, you can specify various options to customize its behavior:

  • args: An array of strings representing the command-line arguments passed to the WASI application.

  • env: An object containing environment variables to be exposed to the application.

  • preopens: An object that maps virtual paths to real paths on the host machine, representing the application's local directory structure.

  • returnOnExit: If true, wasi.start() returns with the WASI application's exit code, otherwise the Node.js process exits.

  • stdin: The file descriptor number used for standard input in the WASI application.

  • stdout: The file descriptor number used for standard output in the WASI application.

  • stderr: The file descriptor number used for standard error in the WASI application.

  • version: The version of WASI requested. Only "unstable" and "preview1" are currently supported.

Real-World Examples

Example 1: Running a WASI Binary from a File

// Import WASI module
import { WASI } from "wasi";

// Load WASI binary from file
const binaryData = fs.readFileSync("my_wasi_binary.wasm");

// Create WASI instance with custom options
const wasi = new WASI({
  args: ["my_wasi_binary"],
  env: { HOME: "/home/user" },
  preopens: { "/data": "/path/to/data" },
});

// Start WASI application
await wasi.start(binaryData);

Potential Applications

WASI enables portable execution of WebAssembly applications across different operating systems and platforms. This can be useful for:

  • Cloud Computing: Deploying WebAssembly workloads to cloud providers like AWS or Azure.

  • Sandboxing: Running potentially malicious code in a secure sandbox environment.

  • Cross-Platform Compatibility: Developing WASI applications that can run on multiple platforms without recompilation.


wasi.getImportObject()

The wasi.getImportObject() method returns an import object that can be passed to WebAssembly.instantiate() if no other WASM imports are needed beyond those provided by WASI.

The import object contains the WASI imports that are required by the WASI runtime. These imports provide access to the WASI API, which allows WASI programs to interact with the underlying operating system.

The import object can be used to instantiate a WASM module that uses the WASI API. For example, the following code instantiates a WASM module that prints "Hello, world!" to the console:

const wasi = new WASI();
const importObject = wasi.getImportObject();

WebAssembly.instantiate(wasmModule, importObject).then((result) => {
  const instance = result.instance;
  instance.exports.main();
});

The wasi.getImportObject() method is a convenient way to get the WASI imports that are required by a WASM module. It can be used to instantiate WASM modules that use the WASI API without having to manually create the import object.

Real-world applications:

The WASI API can be used to develop WASM modules that can interact with the underlying operating system. This makes WASM modules suitable for a variety of applications, including:

  • System programming: WASM modules can be used to develop system programs, such as operating system kernels and drivers.

  • Server-side programming: WASM modules can be used to develop server-side applications, such as web servers and databases.

  • Desktop applications: WASM modules can be used to develop desktop applications, such as games and productivity tools.

  • Mobile applications: WASM modules can be used to develop mobile applications, such as games and social media apps.


WASI.start() Function

What is WASI?

WASI stands for WebAssembly System Interface. It's a set of standards and specifications that allow WebAssembly (WASM) programs to interact with the underlying operating system (OS) in a portable way. In simple terms, WASI allows WASM programs to behave like native programs without relying on the specific details of a particular OS.

What does WASI.start() do?

The WASI.start() function is used to start executing a WASM program as a command within a WASI environment. It takes a WebAssembly.Instance object as its argument.

How WASI.start() Works:

When you call WASI.start(), it does the following:

  1. Checks if the WASM instance has a function called "_start()". If it doesn't, it throws an exception because there's no entry point to start the program.

  2. Checks if the WASM instance has a memory export named "memory". If it doesn't, it throws an exception because the program needs a memory space to work with.

  3. Invokes the "_start()" function of the WASM instance, which is like the main() function in a C program. This function is responsible for starting the execution of the program.

Example of WASI.start():

// Create a WebAssembly instance
const instance = new WebAssembly.Instance(wasmModule, imports);

// Start executing the instance as a command
try {
  WASI.start(instance);
} catch (error) {
  console.error("Error starting WASI program:", error);
}

Real-World Applications:

WASI is used in various applications, including:

  • Cloud Computing: Running WASM programs in cloud environments without OS-specific dependencies.

  • Embedded Systems: Providing a lightweight runtime for WASM programs on resource-constrained devices.

  • Game Development: Enabling cross-platform portability of games and game engines.

  • Secure Computing: Isolating sensitive computations by running them within a WASI environment.


Simplified Explanation:

wasi.initialize() is a function that prepares a WebAssembly instance to run as a WASI program.

Topics:

WebAssembly Instance: A WebAssembly program loaded into memory.

WASI Reactor: A program that follows the WebAssembly System Interface (WASI) specification.

_initialize() Export: A special function in the WebAssembly instance that sets up the WASI environment.

_start() Export: A special function in the WebAssembly instance that starts the program's main logic.

memory Export: A special memory object in the WebAssembly instance that contains the program's data and instructions.

Real-World Application:

WASI allows you to run WebAssembly programs in a more sandboxed and secure environment. This is useful for running untrusted code on platforms like the web or server. For example, you could create a WebAssembly game that runs on a website without giving the game access to the user's personal data.

Complete Code Implementation:

import { WASI } from "wasi";

const instance = new WebAssembly.Instance(module, {
  wasi_snapshot_preview1: {
    // WASI environment settings
    memory: instance.exports.memory,
  },
});

WASI.initialize(instance);

In this code:

  • WASI.initialize() initializes the WebAssembly instance as a WASI reactor.

  • instance.exports.memory is the memory export from the WebAssembly instance.

Potential Applications:

  • Running WebAssembly games in a secure environment on web pages.

  • Isolating and securing sensitive code in server applications.

  • Developing and prototyping cross-platform applications using WebAssembly.


Introduction to wasiImport

wasiImport is an object that allows you to make system calls using the WebAssembly System Interface (WASI), which is a standard for interfacing with operating systems from WebAssembly code.

Passing wasiImport

When you create a WebAssembly instance, you need to pass wasiImport as the wasi_snapshot_preview1 import. This makes WASI functions available to your WebAssembly code.

Making System Calls

To make a system call, you call a function on the wasiImport object. For example, to open a file, you would call wasiImport.fd_open.

Real-World Application

One real-world application of WASI is running serverless functions on WebAssembly. WASI provides a way for WebAssembly code to interact with the operating system, allowing you to create functions that can perform file I/O, network operations, and other system-related tasks.

Improved Code Snippet:

The following code snippet shows how to use wasiImport to open a file:

const instance = new WebAssembly.Instance(module, {
  wasi_snapshot_preview1: wasiImport,
});

const result = await instance.exports.open("test.txt", "r");

console.log(result);

This code creates a WebAssembly instance and passes wasiImport as the wasi_snapshot_preview1 import. Then, it calls the open export function to open a file named "test.txt" for reading. The result of the system call is printed to the console.