自己紹介

佐伯学哉氏(以下、佐伯):Kernel/VM online part4ですが、「ここが変だよWSL2」という日本語タイトルで、スライドは英語になっていますが、WSL2(Windows Subsystem for Linux 2)に関するいろいろなことを話します。

アウトラインですが、基本的にはランダムトークで小ネタをたくさん話します。なので、WSLとは何かとか、技術的には興味深いけれど公式のドキュメントがきちんと説明してること、つまりWSLgですね。技術的にはおもしろいのですが、公式が全部説明しているので、ここでは一切触れません。このトークは、僕が個人的に発見したあまり知られてないバグや、事実についてだけ発表していきます。

軽く自己紹介です。ソフトウェアエンジニアの佐伯といいます。趣味もソフトウェアデベロップメントで、「Distrod」というWSL2でSystemdを動かすメタディストロとか、WSL Hello sudoとか、Dbgeeとか、Noahとか、CPU実験のものとかいろいろやってきました。よろしくお願いします。

UbuntuやArchを動かすようなディストロを制作

前回の「Kernel VM online Part3」で、実はWSLは、自分でディストロを作れるんだよとお話ししました。systemdを簡単なコンテナの中で動かして、その下でUbuntuやArchを動かすようなディストロを作って発表したのが2021年8月のことでした。

その時は「あと2週間くらいで公開します」と言ったのですが、この間やっと公開したので、興味ある方はぜひ使ってください。

これもまた未承諾広告ですが、Distrodというものを作りました。これはlinuxcontainers.orgからディストロのイメージを落としてきて、システムで動かしながらそれをWSLインスタントして動かすソフトになっています。便利なので使ってみてください。

これがDistrodの画面です。Distrodをダウンロードすると、それがDistroインストーラになっているのですが、(スライドを示して)こういう画面が出てきます。

インストールできるディストロの一覧が、Linux Containers.orgから引っ張ってこられて、Arch Linuxとかを入れると、どんどんウィザードに従ってインストールされていきます。

Ubuntuなどをインストールをしたことがあればわかるのですが、WSLの最初の起動画面で、こんな感じのウィザードが出るんですね。それがUbuntuではなくて、linuxcontainers.orgのディストロが何でも選べるようになっています。選んでインストールをすると、そのままArch Linuxが動きます。

普通のディストロとは違って、開発したのはsystemdが動いているというものですね。これはArchです。

WSL2はVMかつコンテナである

その開発でいろいろやったことをお話しするのですが、WSLは実はコンテナなんだよという話とか、InterOpの話とか、あとバグの話とかをしようと思います。

1個目です。WSL2は、WSL1と違って、VMになったとよく説明がされるのですが、実はWSL2はVMかつコンテナなんですね。WSL2はLight-weight VMというのは、まあ正しい。さらにDSL2ディストロは、実はコンテナであるという表現も、実は正しいんです。

探せば、公式のドキュメントにも書いてあると思います。たぶんWSLgのアーキテクチャにも書いてあるのですが、これはあまり知られていないので紹介しようと思います。

WSLディストロがコンテナで動いてることの傍証はたくさんあります。例えば普通にWSLを起動して、ps auxを叩いた時に、カーネルスレッドが一切出ていないんですね。Linuxをネイティブで使ってる人からすると違和感があるのですが、これは新しく作られたPID namespaceで、実は全部のディストロが動いているからなんです。

全部のディストロが、実はそれぞれのコンテナで動いているのですが、/mnt/wslという場所があって、全部のディストロで共有して使えるように、たぶんMS_SHAREDでマウントしてあるんですよね。なので、ここで自分が今動いてるディストロの/procをマウントすることができるんです。

そもそもWSLは、複数のディストロを同時に動かせるのですが、これで何ができるかというと、あるディストロからほかのディストロの中に入るという、遊びができます。

今ここで、Arch Linuxがディストロとして動いているのですが、もう片方がUbuntuですね。Arch Linuxの中で、まずproc/self/mountinfoを見ると、/mnt/wslがshared:1されているのが見えます。

ここに対して、新しくディレクトリを1個作ります。/mnt/wsl/archprocという名前を作って、ここにこのArch Linuxの/procをバインドマウントします。

今度はUbuntuに戻ります。このディストロは、僕が普段使いしているディストロですが、ここで/mnt/wslを見ると、archprocが見えているのがわかります。これはシェアドマウントなので、きちんとバインドマウントしたarchprocの中が見えています。

なので、この/procを使ってnsenterとかして、その各namespaceに入ると、今はそのArch Linuxのマウントnamespaceに入ったのですが、rootとしてArch Linuxのfsが見えているという状況ですね。スクリーンフェッチを動かすと、Arch Linuxが出てきたりします。

なので実はWSLの各ディストロは、WSL自体は1個のVMなのですが、その中の各ディストロはコンテナというおもしろい小ネタです。

WSLのInterOpの仕組み

次にWSLのInterOpについて。WSLは中からexeファイルを動かしたり、外からLinuxのELFを動かしたりできるわけですが、こんな感じでcmd.exeとやるとcmd.exeが動くんですね、Linuxの中から。

これはいったいどうやって動いているかというと、WSLのセッションは、Windowsターミナル1個に対して、タブ1個に対して1個ソケットが生えています。ここに対して、.exeを起動してくれというリクエストをマイクロソフトの/initが通信するんですね。

数字_interopというファイルは、1セッションに対して1個対応するものがあります。それをどうやって探すのか。2パターンあって、あるLinuxがexeを起動する時に、Linux processが環境変数としてWSL_INTEROPというenviroment variableを持っていたら、そのパスがソケットのパスになります。なのでここに出してリクエストが通ります。

一方、それがない状況もあります。例えばsudoを起動すると、環境変数がリセットされてしまうので、WSL_INTEROPがないんですね。どうするかというと、/initはリクエストをたたいたLinux processの親を辿っていきます。やがてそれがWSLのセッション、/initの子どもみたいなものに辿り着くのですが、そのPIDで始まるPID_INTEROPを使うんですね。

なぜそれが動くのか。(スライドを示して)実はこの図のbash、pstree、bashなどの上に、initがいくつも連なっているのが見えると思います。2つ目のinitが、Linuxのsubreaper processとして動いていて、それ以下の子孫のprocessに対して疑似initとして動いているんですね。subreaperという機能があります。

subreaperとは何か。自分のことをsubreaperだと宣言したprocessは、その子孫processに対して本物のPID1の/initのような仕事を代行するという機能があります。WSLはセッションに対して1個1個、そのsubreaperを作っていて、それが必ずそのセッションのサブプロセスの親になっているので、やがてはそこに辿り着いてPID_INTEROPを見つけるという仕組みになっています。

この仕組みを使って、Admin Privilegeも使うことができます。もしWindowsターミナルを管理者権限で立ち上げたのであれば、そのWindowsターミナルだけで動いているLinux processは、そこからexeを起動すると、それは管理者権限のexeプログラムになるという仕組みになっていて、きちんとAdmin権限も使えます。

これは脆弱性ではなくて、セキュリティ上あまり気持ちよくない部分があります。このWSL_INTEROPは、特に認証認可がなくて、勝手に環境変数を変えれば使えるんですよね。

なので、管理者権限のWSL_INTEROPが存在する状況においては、勝手にそのWSL_INTEROPを宣言すると、管理者権限がないターミナル、セッションからでも管理者権限でexeを起動できます。確認済みですが、USCバイパスは脆弱性ではないらしいです。はい、その話はここで終わりです。

ちょっと変なWSLネットワーク

あと1分ですね。メッチャ早く話していきます。WSLネットワークはちょっと変です。static IPが振られて、ネットワークnamespaceが共有されるとか、いろいろあるのですが、とりあえずHyper-Vのネットワークと違ってなんか変です。

パフォーマンスがクソ悪いんですよ。今ここに、あなたのWSLのDNSを殺すコマンドがあるのですが、30回連続でdigを叩くだけで、DOSが成立して、WSLのDNSがしばらく死ぬんですね。

30回digを叩くだけでは普通はHyper-VのDNSもWindowsのDSNも死ぬことはないので、なぜかこれはWSLだけで起こる謎のパフォーマンスイシューです。まあ8.8.8.8とかにすると回避できますという感じですね。

バグはほかにもあります。これは誰も書いていないのですが、WSLはWindowsの起動時に起動しようとすると、タスクスケジューラでWindowsスタートアップでWSLを起動するタスクを組むのが定石ですが、どこのサイト見ても「Highest Privlilegesで動かす必要があります」と書いてあるんですね。

ところがこれは嘘で、これはWSLコマンドのバグです。直接WSLのAPIを叩くようなPowershellコマンドを書いて、それを起動時に立ち上げると、管理者権限なしでもWindowsスタートアップ時にWSLを起動できます。先ほどの管理者権限の絡みであまり管理者権限で長いprocess動かしたくないので、こちらのほうが安全です。

最後に。WSLの変な点を話したのですが、基本的にはWSLは便利でクールなので、いろいろ言ったけど僕はWSL大好きです。これからもMSさんにはがんばってほしいなと思っています。はい、以上です。

質疑応答

司会者:ありがとうございます。小ネタ集はやはりおもしろいですね。ちなみにWSL2は開発もオープンなんでしたっけ? コードがオープンになってたような気がするんですけど。

佐伯:開発はオープンではなくて、Linux Kernelだけオープンなんですね。

司会者:ははあ、上物だけなんだ。

佐伯:memory reclaimと言うのですが、メモリをWindowsに返すというKernelモジュールがあります。その部分だけオープンソースで開発されています。GPLですね。

司会者:なるほど。じゃあ改良とか。

佐伯:そうですね。

司会者:改良とか、そういうテクニカルリクエストは、いわゆるプルリクエストみたいなものを自分で直接は送れなくて、よくてリクエストが出せるぐらいですね。

佐伯:そうですね。でもそもそも、そのメモリリクレーム以外のコードは基本的にオープンではなくて、先ほど動いたコンテナとして動いているとか、そのスラッシュユニットとかは全部クローズドソースです。

司会者:ふむふむ。

佐伯:GPLが及んでいない部分に関しては、基本的にコードは公開されていないです。

司会者:なるほど。ありがとうございます。