今回のテーマは「Kernel/VM的WebAssembly入門」

佐伯学哉氏(以下、佐伯):佐伯が『WebAssemblyのWeb以外のこと全部話す』というタイトルで発表します。

まず、WebAssemblyとは何ぞや? という一般的な話なのですが、「Wikipedia」からの引用によれば、「Webブラウザーのクライアントサイドスクリプトとして動作する低水準言語である。ブラウザー上でバイナリフォーマットのかたちで実行可能であることを特徴とする」とあります。

実際の応用例としては、WebでGoogle Meetの背景ぼかしに使われていたり、「Figma」「AutoDesk」「Google Earth」、ゲームなどでも使われています。こういう用途でWebAssemblyは期待されていてメチャクチャ有名です。

ですが、今日はそういう話は一切しません。今日のフォーカスするテーマはWebの外のWasm(WebAssembly)、もしくはブラウザーの外のWasmと呼ばれるものです。いわばKernel/VM的WebAssembly入門というかたちで始めます。

WebAssemblyに存在するアツいフロンティア

WasmのWebの応用についてはけっこう話を聞くんですが、Webの外の応用はあまり聞かないので、ざっくり具体例を紹介します。

一番上に、Wasmerというものがあります。WasmerはWebAssemblyにコンパイルされたバイナリを、Webブラウザーではなくてネイティブのデスクトップで動かすためのランタイムです。EmscriptenやWASIターゲット向けにコンパイルされたWebAssemblyが、デスクトップやラップトップで動きます。

Cで書いてWebブラウザーにコンパイルしたかったものを、コンパイルしたあとに、またデスクトップで動かすみたいな、よくわからないことをやっているように見えるので、倒錯して聞こえるんですがそういうものです。

2個目はEnvoyです。Envoyは、Kubernetesクラスタでよく使われるNGINXみたいなプロキシのものです。パケットやHTTPリクエストの改変や、許可するか拒否するかなどをWasmプラグインで書けます。

ほかにもKrustletといって、Kubernetes上で普通のコンテナではなくてWasmコンテナを実現するものもあります。だんだんクレイジーさが上がっていって、Kernel-wasmというLinuxカーネル内で、コンテキストスイッチなしでWebAssemblyバイナリを動かすものや、Nabuletという、WebAssemblyのmicrokernelを高速に実現して安全にすべてのプロセスを単一メモリ空間でやるぞみたいなものもあります。

説明だけ聞くと、一発ギャグに聞こえるのですが、そうではないという話を今日はします。WebAssemblyにはアツいフロンティアが存在します。

今日のテーマは「WasmのWeb以外のこと全部話す」です。Web外WebAssemblyがなぜアツいのか、また、WebAssemblyがWebの外でどう使われているのかを話します。ほかにも、Web外Wasmのエコシステムがどんなものかを話します。WasmのメインフィールドであるWebについてや、Wasmの詳細な基本仕様についてはお話ししません。

Wasmが夢見る世界は複合的な側面を包括している

さっそくメインテーマの「何がWasmをそんなにアツくさせるのか」です。Web外Wasmのユースケースや、Web外Wasmがどうしてそんなにアツいのかという話ですが、先ほどの例は、実はWasmが夢見る世界の具体例になっています。Wasmが目指す世界は複合的な側面をもっていて、まず1つ目はJavaが目指したものです。portable executableファイルとしてのJavaが目指したものがWasmが実現しているものの1つです。

さらにこれはあまり有名ではないのですが、Microsoftが.NETで一応今もやっているcommon language bytecodeインターフェイス、common languageインターフェイスという、いろいろな言語が1つのVMで動くというものがあって、その側面もあります。

ほかにも、NaClやeBPFが目指すsecureでisolatedされたsandboxの中でUntrustedなバイナリも動かせるようにしたいというものがあります。もしくはLuaみたいにどこかに埋め込んでスクリプトを実行させたいとか、Java、CLI、NaCl、eBPF、Luaが目指した、もしくは実際に少し実現している世界を、すべて複合的になんとかしたいという夢を見るというのが、Wasmの複合的な側面です。

先ほどWasmerなど、WebAssemblyをポータブルにバイナリとしていろいろな大きさで動かすという話があったと思うのですが、そういうものは「これはJVMをもう1度やっているんだな」とか、もしくはsecureなsandboxの文脈で「これはeBPFをもう1回やっているんだな」と理解すると、複合的な側面のほかの側面のアツさを見逃してしまいます。この複合的な側面すべてを包括しているのが、WebAssemblyがアツい理由です。

Wasmをアツくする4つのデザインゴール

Wasmにはさまざまなデザインゴールがあって、これは公式サイトから引用してきた13個のデザインゴールです。今回フォーカスするデザインゴールは、この中で「Safe」「Language-independent」「Platform-independent」と、たぶん最後は言葉だけ聞くとよくわからないと思うんですが、「Open」という部分の4つにフォーカスしてお話しします。

1個目のSafeはどういうことかというと、UntrustedなWebAssemblyを安全に動かせるという意味でのSafeです。これはeBPFやNaClがやっている部分になりますね。この側面に関しては、JVMはある種ただの実行ファイルなので、JVMでは決してUntrustedなコードを動かす機能は実現できません。

次に、Language-independentです。CやC++を含めた低レベルな言語を含めて、言語を選ばない状態で、WasmのVMでは動くことができます。これもJVMやeBPFでは現実的には難しい部分です。JVMにはGraalみたいな話もあるんですが、いろいろあって、実際にどうなるかは難しいかもなと個人的には思っています。

Graalは、LLVM言語を動かす時にLLVMbitcodeをインタプリタで動かしているらしいのですが、LLVMbitcodeは安定しなくて難しいというのが昔PNaClが辿った道なので、現実的にはどうかなと思っています。GraalじゃなくてもJVMという単体で見れば、もともとのJVMのゴールはそこではないですね。

3つ目のPortable/Platform-independentですが、これは逆にJVMに似た話で、環境やOSを選びません。NaClはx86バイナリなので、厳しかったものです。

最後のOpenですが、Openとはホスト環境とのAPIインターフェイスがオープンであることを意味しています。つまり、組み込みデバイスではなくて、あるシステムの中に組み込むスクリプティング環境として使いやすいという話です。

これに関しては、今だとLuaがかなり活用している分野なのですが、Lua以外だと決定的な選択肢が存在しなかったと認識しています。

このような理由でWasmがアツいのですが、さらにこの4つの複合的な側面をもつことで、今までこの4つの需要に対してそれぞれいろいろなステークホルダーが投資してきた部分が1つのWebAssemblyに投資が集中するので、エコシステムの発展が早いのも魅力です。

Wasmの言語仕様 Language/Platform-independent

先ほどの4つの特徴からWebAssemblyの言語仕様をちょっと訪問して、どうやってそのアツい特徴をWebAssemblyでは実現しているのかを見てみましょう。

1つ目のLanguage/Platform-independentは簡単ですね。WebAssemblyは低レベルなスタックマシンのバイナリバイトコードなので、JVMやCLIと違ってGCとかobject-oriented programmingの概念もなく、CとかC++とかをコンパイルしやすいです。

ハーバードアーキテクチャで、基本VMランタイムで動くので当然Platform-independentにもなります。

また、言語仕様が小さいこともけっこう重要です。JVMのVMのように大きな言語仕様を策定していないので、さまざまなコンパイラがバックエンドの1ターゲットとしてサポートできます。

サードパーティが、丸々言語をWasmに移植してみたみたいなケースもありますが、そうではなくて多くの場合、その言語のToolchain自体がバックエンドとしてWasmをサポートできます。そしてホストアーキテクチャに依存しないという話ですね。

Wasmの言語仕様 Safe

3つ目のSafeですが、そもそもSafeは何かをきちんと定義しないとあまり意味がある言葉ではありません。Wasmの場合は、VMを動かしているOSだったり、ブラウザーだったりのホストランタイムから隔離された単一メモリ空間をもち、しかもハーバードアーキテクチャであることでSafeであると言われます。どうしてかというと、デフォルトでは外界になんの副作用も行えないからです。

なので、WasmのVMの中にどれだけmaliciousなプログラムがあったとしても、そこでできるのは計算することだけなので、せいぜいビットコインを掘るくらいしかできません。変なシステムコールを呼ぶとかは標準ではできないので、そういう意味でWasmはSafeになります。

これは若干言語仕様の話で、そこまで本質的ではないのですが、Structuredな言語構造しかないという特徴もあります。普通のAssemblyでいうgotoとかがなくて、blockやloopなどの概念があって、Basic Blockまでしかジャンプできなかったり、ローカル変数があったりします。

スタックの概念がVMで管理されていて、Control Flow IntegrityがVMレベルでは確保されているなどの違いもあります。構造化プログラミング以後に生まれたアセンブリ言語だと思ってくださいと書いていますが、本当かはちょっとわからないです。昔はif文が書けるアセンブリ言語みたいなものがあったらしいですね。

気になっている方もいるかもしれないので、ホストランタイムからメモリ空間を隔離するのはいったいどうするのかお話しします。VMの場合は簡単です。VMで動いている場合は、VMのメモリ空間はVMが管理しているので、基本的にホストランタイムのメモリが漏れることはありません。

コンパイルする場合は、software fault isolationの仕組みをうまく使える仕様になっていて、メモリ空間が32ビットしかないので、4GBとその上下のページを確保しておけば、どんなにmaliciousバイナリであってもホストのそれ以外のメモリ空間で、そういうことはできない仕様になっています。

このような仕組みによって、UntrustedなコードをWasm VM上、AOT上で動かすことができます。

Wasmの言語仕様 Open

最後の4つ目の特徴です。実はWasmは、オブジェクトファイルフォーマットの仕様を含むという重要な仕様が存在します。どういうことかというと、WebAssemblyという名前なんですが、ただの言語仕様ではなくて、右の図のようにELFのようなオブジェクトファイルフォーマットの仕様が決まっています。

こういうことを考えると、WasmはよくWebでもなければアセンブリでもないと言われるんですが、なんなら言語仕様でもない可能性があります。そういう与太話があり得ます。

オブジェクトフォーマットで重要なのは、このフォーマットの中にImportするものとExportするものを定義する場所があるということです。具体的には、関数や変数を定義することができます。ここで、ランタイムから何をもらうか、ランタイムに何を渡すかを定義できます。

重要なのは、Wasmはそこで具体的に何を受け渡しするかについて、仕様では定めていないんという点ですね。最も一般的なのは、ブラウザーのJSランタイムと通信するためのglueのAPIですが、ここ自体は実はWasmのフォーマットでは触っていなくて、自由に組み替えることでいろいろな応用先が生まれるというおもしろさがあります。このことに関しては、似たものがあまりないのですが、現在Luaが活躍しているフィールドです。

Wasmができることは外のインターフェイスが決める

Openなだけではいろいろと大変なんですが、さらにWebAssemblyは先ほどお話ししたようにPortableで、しかもLanguage-independentでSafeなので、Untrustedなコードでも組み込んで動かせるといういろいろな応用があって、Wasmに多様な応用を生んでいます。

Openであることによって生まれる話なんですが、先ほどお話したように実はWasmは、単体だけでは何ができるかは決まっていません。

Wasmはオブジェクトフォーマットであって、ELFというLinuxのオブジェクトファイルが実際何ができるかは、POSIX、Linux、BSDのシステムコールが決めているように、外のインターフェイスが決めます。

普通はブラウザーの中でWeb APIやJSのグルー APIが存在します。ほかにもWASIというOSとWasm単体バイナリが通信するためのインターフェイスとか、Proxy-Wasmというプロキシの中で動くためのインターフェイスとか、その他のおもしろインターフェイスとして、カーネルの中でWasmをBPFライクに動かすためのインターフェイスや、Ethereumのランタイムもあります。また、研究レベルではTrusted execution Environment(TEE)をなにかしようという話もあるらしいです。

このような4つの特徴があります。そこらへんのことをすべて考えてみて複合的なプロダクトでWasmがあることを考えると、実はWasmは、既存技術のJVMやeBPFのリバイバルではないというのが重要な話、かつ、Wasmがアツい理由です。

(次回へつづく)