Skip to content

WebAssembly システムインターフェース (WASI)

[安定版: 1 - 実験的]

安定版: 1 安定性: 1 - 実験的

node:wasi モジュールは、現在、一部の WASI ランタイムによって提供される包括的なファイルシステムセキュリティプロパティを提供していません。安全なファイルシステムサンドボックスの完全なサポートは、将来実装される場合とされない場合があります。当面の間、信頼できないコードの実行には使用しないでください。

ソースコード: lib/wasi.js

WASI API は、WebAssembly システムインターフェース仕様の実装を提供します。WASI は、POSIX に似た関数のコレクションを介して、WebAssembly アプリケーションに基礎となるオペレーティングシステムへのアクセスを提供します。

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)
})()

上記の例を実行するには、demo.watという名前の新しい WebAssembly テキストフォーマットファイルを作成します。

text
(module
    ;; 与えられた io vectors を stdout に書き込むために必要な fd_write WASI 関数をインポートします
    ;; 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))

    ;; 8 バイトのオフセットにあるメモリに 'hello world\n' を書き込みます
    ;; テキストが表示されるためには、末尾の改行文字が必要です
    (data (i32.const 8) "hello world\n")

    (func $main (export "_start")
        ;; 線形メモリ内に新しい io vector を作成します
        (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 - stdout は 1
            (i32.const 0) ;; *iovs - メモリ位置 0 に格納されている iov 配列へのポインタです
            (i32.const 1) ;; iovs_len - iov に格納されている文字列を 1 つ出力しています - したがって 1 です。
            (i32.const 20) ;; nwritten - 書き込まれたバイト数を格納するメモリ内の場所です
        )
        drop ;; スタックの一番上から書き込まれたバイト数を破棄します
    )
)

wabt を使用して .wat.wasm にコンパイルします。

bash
wat2wasm demo.wat

セキュリティ

[履歴]

バージョン変更点
v21.2.0, v20.11.0WASI のセキュリティ特性を明確化しました。
v21.2.0, v20.11.0追加:v21.2.0, v20.11.0

WASI は、アプリケーションに独自のenvpreopensstdinstdoutstderrexit機能を提供する、機能ベースのモデルを提供します。

現在の Node.js の脅威モデルは、一部の WASI ランタイムにあるような安全なサンドボックスを提供していません。

機能はサポートされていますが、Node.js ではセキュリティモデルを形成していません。たとえば、ファイルシステムのサンドボックスは様々な手法で回避できます。このプロジェクトでは、将来これらのセキュリティ保証を追加できるかどうかを検討しています。

クラス: WASI

追加: v13.3.0, v12.16.0

WASIクラスは、WASI システムコール API と、WASI ベースのアプリケーションを操作するための追加の便利なメソッドを提供します。各WASIインスタンスは、異なる環境を表します。

new WASI([options])

[履歴]

バージョン変更点
v20.1.0returnOnExit のデフォルト値が true に変更されました。
v20.0.0version オプションが必須となり、デフォルト値はなくなりました。
v19.8.0options に version フィールドが追加されました。
v13.3.0, v12.16.0追加: v13.3.0, v12.16.0
  • options <Object>
    • args <Array> WebAssembly アプリケーションがコマンドライン引数として認識する文字列の配列。最初の引数は、WASI コマンド自体の仮想パスです。デフォルト: []
    • env <Object> WebAssembly アプリケーションがその環境として認識するprocess.envに似たオブジェクト。デフォルト: {}
    • 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 のバージョン。現在サポートされているバージョンはunstablepreview1のみです。このオプションは必須です。

wasi.getImportObject()

追加: v19.8.0

WASI によって提供されるもの以外に WASM インポートが不要な場合、WebAssembly.instantiate()に渡すことができるインポートオブジェクトを返します。

バージョンunstableがコンストラクタに渡された場合、以下を返します。

json
{ wasi_unstable: wasi.wasiImport }

バージョンpreview1がコンストラクタに渡された場合、またはバージョンが指定されなかった場合、以下を返します。

json
{ wasi_snapshot_preview1: wasi.wasiImport }

wasi.start(instance)

追加: v13.3.0, v12.16.0

_start()エクスポートを呼び出すことによって、instanceを WASI コマンドとして実行を開始しようとします。instance_start()エクスポートが含まれていない場合、またはinstance_initialize()エクスポートが含まれている場合、例外がスローされます。

start()には、instancememoryという名前のWebAssembly.Memoryをエクスポートすることが必要です。instancememoryエクスポートがない場合、例外がスローされます。

start()が複数回呼び出された場合、例外がスローされます。

wasi.initialize(instance)

追加: v14.6.0, v12.19.0

存在する場合、その_initialize()エクスポートを呼び出すことによって、instanceを WASI リアクターとして初期化しようとします。instance_start()エクスポートが含まれている場合、例外がスローされます。

initialize()には、instancememoryという名前のWebAssembly.Memoryをエクスポートすることが必要です。instancememoryエクスポートがない場合、例外がスローされます。

initialize()が複数回呼び出された場合、例外がスローされます。

wasi.wasiImport

追加: v13.3.0, v12.16.0

wasiImportは、WASI システムコール API を実装するオブジェクトです。このオブジェクトは、WebAssembly.Instanceのインスタンス化時にwasi_snapshot_preview1インポートとして渡される必要があります。