Skip to content

WebAssembly System Interface (WASI)

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1 - Экспериментально

Модуль node:wasi в настоящее время не обеспечивает всеобъемлющих свойств безопасности файловой системы, предоставляемых некоторыми средами выполнения WASI. Полная поддержка безопасного песочницы файловой системы может быть реализована в будущем, а может и нет. Тем временем не полагайтесь на него для запуска ненадежного кода.

Исходный код: lib/wasi.js

API WASI предоставляет реализацию спецификации WebAssembly System Interface. WASI предоставляет приложениям WebAssembly доступ к базовой операционной системе через набор функций, подобных POSIX.

js
import { readFile } from 'node:fs/promises'
import { WASI } from 'node:wasi'
import { argv, env } from 'node:process'

const wasi = new WASI({
  version: 'preview1',
  args: argv,
  env,
  preopens: {
    '/local': '/some/real/path/that/wasm/can/access',
  },
})

const wasm = await WebAssembly.compile(await readFile(new URL('./demo.wasm', import.meta.url)))
const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject())

wasi.start(instance)
js
'use strict'
const { readFile } = require('node:fs/promises')
const { WASI } = require('node:wasi')
const { argv, env } = require('node:process')
const { join } = require('node:path')

const wasi = new WASI({
  version: 'preview1',
  args: argv,
  env,
  preopens: {
    '/local': '/some/real/path/that/wasm/can/access',
  },
})

;(async () => {
  const wasm = await WebAssembly.compile(await readFile(join(__dirname, 'demo.wasm')))
  const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject())

  wasi.start(instance)
})()

Для запуска приведенного выше примера создайте новый файл в формате текста WebAssembly с именем demo.wat:

text
(module
    ;; Импорт необходимой функции WASI fd_write, которая будет записывать заданные векторы ввода-вывода в stdout
    ;; Сигнатура функции для fd_write:
    ;; (Дескриптор файла, *iovs, iovs_len, nwritten) -> Возвращает количество записанных байтов
    (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))

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

    ;; Запись 'hello world\n' в память с смещением 8 байт
    ;; Обратите внимание на завершающий символ новой строки, который необходим для отображения текста
    (data (i32.const 8) "hello world\n")

    (func $main (export "_start")
        ;; Создание нового вектора ввода-вывода в линейной памяти
        (i32.store (i32.const 0) (i32.const 8))  ;; iov.iov_base - Это указатель на начало строки 'hello world\n'
        (i32.store (i32.const 4) (i32.const 12))  ;; iov.iov_len - Длина строки 'hello world\n'

        (call $fd_write
            (i32.const 1) ;; file_descriptor - 1 для stdout
            (i32.const 0) ;; *iovs - Указатель на массив iov, который хранится в памяти по адресу 0
            (i32.const 1) ;; iovs_len - Мы печатаем 1 строку, хранящуюся в iov - поэтому один.
            (i32.const 20) ;; nwritten - Место в памяти для хранения количества записанных байтов
        )
        drop ;; Отбрасываем количество записанных байтов с вершины стека
    )
)

Используйте wabt для компиляции .wat в .wasm

bash
wat2wasm demo.wat

Безопасность

[История]

ВерсияИзменения
v21.2.0, v20.11.0Уточнены свойства безопасности WASI.
v21.2.0, v20.11.0Добавлено в: v21.2.0, v20.11.0

WASI предоставляет модель на основе возможностей, с помощью которой приложениям предоставляются собственные возможности env, preopens, stdin, stdout, stderr и exit.

Текущая модель угроз Node.js не обеспечивает безопасную изоляцию, как в некоторых средах выполнения WASI.

Хотя функции возможностей поддерживаются, они не образуют модель безопасности в Node.js. Например, песочница файловой системы может быть обойдена с помощью различных методов. Проект изучает возможность добавления этих гарантий безопасности в будущем.

Класс: WASI

Добавлено в: v13.3.0, v12.16.0

Класс WASI предоставляет API системных вызовов WASI и дополнительные удобные методы для работы с приложениями на основе WASI. Каждый экземпляр WASI представляет собой отдельную среду.

new WASI([options])

[История]

ВерсияИзменения
v20.1.0Значение по умолчанию для returnOnExit изменено на true.
v20.0.0Опция version теперь обязательна и не имеет значения по умолчанию.
v19.8.0Добавлен параметр version в options.
v13.3.0, v12.16.0Добавлено в: v13.3.0, v12.16.0
  • options <Object>
    • args <Array> Массив строк, которые WebAssembly-приложение будет видеть как аргументы командной строки. Первый аргумент — виртуальный путь к самой команде WASI. По умолчанию: [].
    • env <Object> Объект, похожий на process.env, который WebAssembly-приложение будет видеть как свою среду. По умолчанию: {}.
    • preopens <Object> Этот объект представляет локальную структуру каталогов WebAssembly-приложения. Строковые ключи preopens рассматриваются как каталоги в файловой системе. Соответствующие значения в preopens — это реальные пути к этим каталогам на хост-машине.
    • returnOnExit <boolean> По умолчанию, когда WASI-приложения вызывают __wasi_proc_exit(), wasi.start() вернет указанный код выхода, а не завершит процесс. Установка этого параметра в false приведет к завершению процесса Node.js с указанным кодом выхода. По умолчанию: true.
    • stdin <integer> Дескриптор файла, используемый в качестве стандартного ввода в WebAssembly-приложении. По умолчанию: 0.
    • stdout <integer> Дескриптор файла, используемый в качестве стандартного вывода в WebAssembly-приложении. По умолчанию: 1.
    • stderr <integer> Дескриптор файла, используемый в качестве стандартного потока ошибок в WebAssembly-приложении. По умолчанию: 2.
    • version <string> Запрашиваемая версия WASI. В настоящее время поддерживаются только версии unstable и preview1. Этот параметр обязателен.

wasi.getImportObject()

Добавлено в: v19.8.0

Возвращает объект импорта, который может быть передан в WebAssembly.instantiate(), если не требуются другие импорты WASM, помимо тех, которые предоставляются WASI.

Если в конструктор был передан параметр unstable, будет возвращено:

json
{ wasi_unstable: wasi.wasiImport }

Если в конструктор был передан параметр preview1 или версия не указана, будет возвращено:

json
{ wasi_snapshot_preview1: wasi.wasiImport }

wasi.start(instance)

Добавлено в: v13.3.0, v12.16.0

Пытается начать выполнение instance как команды WASI, вызывая её экспорт _start(). Если instance не содержит экспорта _start() или содержит экспорт _initialize(), то выбрасывается исключение.

start() требует, чтобы instance экспортировал WebAssembly.Memory с именем memory. Если instance не имеет экспорта memory, выбрасывается исключение.

Если start() вызывается более одного раза, выбрасывается исключение.

wasi.initialize(instance)

Добавлено в: v14.6.0, v12.19.0

Пытается инициализировать instance как реактор WASI, вызывая его экспорт _initialize(), если он присутствует. Если instance содержит экспорт _start(), то выбрасывается исключение.

initialize() требует, чтобы instance экспортировал WebAssembly.Memory с именем memory. Если instance не имеет экспорта memory, выбрасывается исключение.

Если initialize() вызывается более одного раза, выбрасывается исключение.

wasi.wasiImport

Добавлено в: v13.3.0, v12.16.0

wasiImport — это объект, реализующий API системных вызовов WASI. Этот объект должен передаваться как импорт wasi_snapshot_preview1 во время создания экземпляра WebAssembly.Instance.