WebAssembly System Interface (WASI)#

安定性: 1 - 実験的

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

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

WASI API は、WebAssembly System Interface 仕様の実装を提供します。WASI は、WebAssembly アプリケーションに POSIX のような関数のコレクションを介して、基盤となるオペレーティングシステムへのアクセスを提供します。

import { readFile } from 'node:fs/promises';
import { WASI } from '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);'use strict';
const { readFile } = require('node:fs/promises');
const { WASI } = require('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 テキスト形式ファイルを作成します

(module
    ;; Import the required fd_write WASI function which will write the given io vectors to stdout
    ;; The function signature for fd_write is:
    ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written
    (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))

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

    ;; Write 'hello world\n' to memory at an offset of 8 bytes
    ;; Note the trailing newline which is required for the text to appear
    (data (i32.const 8) "hello world\n")

    (func $main (export "_start")
        ;; Creating a new io vector within linear memory
        (i32.store (i32.const 0) (i32.const 8))  ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string
        (i32.store (i32.const 4) (i32.const 12))  ;; iov.iov_len - The length of the 'hello world\n' string

        (call $fd_write
            (i32.const 1) ;; file_descriptor - 1 for stdout
            (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0
            (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
    )
) 

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

wat2wasm demo.wat 

セキュリティ#

WASI は、アプリケーションに独自のカスタムの envpreopensstdinstdoutstderr、および exit 機能を提供する機能ベースのモデルを提供します。

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

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

クラス: WASI#

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

new WASI([options])#

  • 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()#

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

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

{ wasi_unstable: wasi.wasiImport } 

コンストラクターに preview1 バージョンが渡された場合、またはバージョンが指定されていない場合は、次を返します

{ wasi_snapshot_preview1: wasi.wasiImport } 

wasi.start(instance)#

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

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

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

wasi.initialize(instance)#

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

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

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

wasi.wasiImport#

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