Chromium プロジェクトが Rust の利用をサポート
2023年2月16日木曜日
近いうちに、Chromium プロジェクトで C++ からサードパーティ製 Rust ライブラリを使用することがサポートされます。現在(元記事公開当時)、ビルドシステムにプロダクション Rust ツールチェーンを追加する作業を懸命に進めています。来年中には Chrome バイナリに Rust コードを含めることができるようになる予定です。この取り組みは徐々に開始し、準備ができたときに検討の対象となるライブラリを明確に予測する作業を行っています。
このブログ投稿では、現時点(元記事公開当時)でサードパーティ製 Rust ライブラリをサポートしつつも、Chromium で Rust を幅広く使うことはしないという決断に至った経緯について説明します。
Chromium に Rust を導入する理由
Chromium に Rust を導入する目的は、シンプルで(IPC を使わない)安全な(全般的に C++ よりも複雑でなく、サンドボックスでメモリの安全性に関するバグは発生しない)形で 2 の法則を満たすことにより、Chrome の開発をスピードアップし(書くコードを減らし、設計ドキュメントを減らし、セキュリティ レビューを減らす)、セキュリティを改善する(メモリの安全性に関するバグのないコードの行数を増やし、コードのバグ密度を減らす)ことにあります。そしてこの目的に向かうために、サードパーティ製 Rust ライブラリが役立つものと確信しています。Rust は、Mozilla がブラウザを書くために特別に開発したものなので、このテクノロジーが Chromium でようやく使われ始めるようになるのは、理にかなったことです。システム ソフトウェア業界に莫大な貢献をしてくださっている Mozilla に感謝いたします。Rust は、安全性とパフォーマンスの両方を言語に期待できることを見事に証明しています。
C++ と Rust は、cxx、autocxx、bindgen、cbindgen、diplomat、(試験運用版の)crubit などのツールを使ってうまく連携できることがわかっています。ただし、制限事項もあります。やがて新しいツールや改良されたツールによって、こういった制限事項の状況は変わるものと期待できますが、今回の決断や説明は現在のテクノロジーの状況に基づいています。
C++ と Rust は、cxx、autocxx、bindgen、cbindgen、diplomat、(試験運用版の)crubit などのツールを使ってうまく連携できることがわかっています。ただし、制限事項もあります。やがて新しいツールや改良されたツールによって、こういった制限事項の状況は変わるものと期待できますが、今回の決断や説明は現在のテクノロジーの状況に基づいています。
Chromium が Rust の利用をサポートする方法
Chrome セキュリティ チームは、どのように Rust と C++ のコードの併用にアプローチすべきか、時間をかけて調査してきました。Google のソフトウェア スタックでも、C++ ではなく Rust を記述する時間が増えていることの意味を理解し、安全かつシンプルで信頼性の高い相互利用の制限とはどのようなものかを把握するためです。
この調査に基づき、Chromium の方向性について 2 つの結論を出しました。
- 現時点では、C++ から Rust への一方通行の利用のみをサポートします。Chromium は C++ で書かれており、main() から exit() までのスタック フレームの大半は C++ コードです。前述の方向を選んだ理由はここにあります。利用を一方向に限定することで、依存関係ツリーの形を制御します。Rust は C++ に依存することはできないので、依存関係の注入を除けば、Rust が C++ の型や関数を知ることはできません。こうすることで、Rust は任意の C++ コードではなく、C++ から API を通して渡された関数だけを実行できるようになります。
- 現時点では、サードパーティ製ライブラリのみをサポートします。サードパーティ製ライブラリはスタンドアロン コンポーネントとして書かれたもので、Chromium の実装についての暗黙的な知識は持ち合わせていません。つまり、シンプルで 1 つのタスクに特化した API を提供しています。言い換えれば、通常はインターフェースが狭く、複雑なポインタグラフや共有所有権は存在しません。C++ で使用するライブラリのレビューし、期待を満たすものであることを確認します。
Chromium での Rust と C++ の相互利用
これまで成功を収めてきたほとんどの C/C++ と Rust の相互利用事例は、狭い API(例 : QUIC や bluetooth のライブラリ、Linux ドライバ)や明確に分離されたコンポーネント(例 : IDL、IPC)によるものが中心でした。Chrome では、//content/public レイヤなど、基本的ではあるものの実に広範な C++ API が使われています。私たちは、こういった種類の API に対して Rust コンポーネントを構築することがどのような意味を持つのかについて検討しました。高レベルでは、C++ と Rust は異なるルールで動作するため、とても簡単に横道にそれてしまう可能性があることがわかりました。
概念的にまとめると、別の相互利用ツールのサポートがない段階では、次のようになります。
たとえば、Rust は生存期間(推論または明示的記述による)と排他的可変性という 2 つの入力を使った静的解析で一時メモリの安全性を保証します。後者は、Chromium の大半の C++ の記述方法と互換性がありません。冗長な可変ポインタや、可変ポインタに到達する複数のパスを提供するポインタは、システムのいたるところに存在しています。循環する可変データ構造も存在します。(可変)ポインタが相互に連結した巨大な構造が含まれているブラウザのプロセスには、これが特に当てはまります。こういった C++ ポインタが複雑かつ長時間存在する形で Rust 参照にも使われるとすれば、C++ 作成者が Rust のエイリアス ルールを理解し、たとえば次のような方法で、それに違反する可能性を除かなければならなくなります。
- 1 つの関数から同じ可変ポインタを 2 回返し、1 つを保持できるようにする。
- 重複する複数のポインタの片方は可変として Rust に渡し、参照として同時に保持できるようにする。
- 共有または可変の参照を通して、Rust から状態の変化が見えるようにする。
コンパイラや型システムを通してサポートを提供する相互利用ツールがない場合、デベロッパーは Rust コンパイラが前提とする内容をすべて理解し、C++ 側からそれに違反することがないようにしなければなりません。この枠組みでは、C++ は安全でない Rust と同じであるも同然です。また、安全でない Rust はプロジェクトにとってコストがかかるものですが、そのコストはカプセル化を維持し、最低限にとどめることによって管理できます。同様に、C++ は非常に複雑なので、安全な Rust からカプセル化する必要があります。相互利用のために設計した狭い API は、同じようなカプセル化を提供できます。私たちは、言語間で広い API を許可できるような別の方法で、相互利用ツールがカプセル化を提供できるようになることを期待しています。
概念的にまとめると、別の相互利用ツールのサポートがない段階では、次のようになります。
- 言語間でのポインタ渡しや参照渡しにはリスクがある。
- 現実的に正しいコードを書けるようにするには、言語間の狭いインターフェースが重要である。
片方の言語のコンセプトがもう片方にない場合、言語間で任意のコードを相互利用できるようにすると、困難な状況が発生します。Rust が C++ を呼ぶ場合、バインディング ジェネレータではテンプレートや継承といった言語機能をサポートするのが難しいことがあります。C++ が Rust を呼ぶ場合、proc マクロや trait などが同じ課題をもたらします。インピーダンスの不整合は、どちらかの言語でそのような設計が意図的に選択された結果である場合がありますが、言語間の FFI(相互利用)を制限するものでもあります。各言語の考え方を他者にとって妥当な形でモデリングするか、それを許可しないようにする相互利用ツールが必要です。
Chromium から Rust エコシステムにアクセスする
こういった課題は、相互利用を簡単かつシームレスにするための機会であると同時に、両方の言語の幅広いライブラリにアクセスする機会でもあります。Google は Crubit に取り組んでいます。これは、C++ と Rust との相互利用の忠実度を上げ、各言語がもう一方に求める要件を表現したり、カプセル化したりする方法を探る実験です。
Chromium のようなセキュリティを重視したオープンソース プロジェクトにとって、Rust エコシステムはとりわけ重要な存在です。このエコシステムは巨大で(crates.io には 96,000 以上のクレートがあります)、Google を含むシステム開発業界からの大規模な投資を受けて成長を続けています。Chrome はサードパーティ製コードを多用しているので、サードパーティへの投資が行われている場所を把握する必要があります。私たちにとって重要なのは、Rust を Chromium プロジェクトに組み込む対応です。
Chromium のようなセキュリティを重視したオープンソース プロジェクトにとって、Rust エコシステムはとりわけ重要な存在です。このエコシステムは巨大で(crates.io には 96,000 以上のクレートがあります)、Google を含むシステム開発業界からの大規模な投資を受けて成長を続けています。Chrome はサードパーティ製コードを多用しているので、サードパーティへの投資が行われている場所を把握する必要があります。私たちにとって重要なのは、Rust を Chromium プロジェクトに組み込む対応です。
私たちは、この戦略に従って基準を定め、サードパーティのプロセスを通じて API レビューのレベルを維持する予定です。同時に、相互利用サポートの未来を見据え、Rust と C++ の間で合理的に行えることの限界を押し上げていきます。
その他の関連コンテンツ
メモリが安全でないことは業界全体の問題であり、この領域で変化を起こす戦略の 1 つが Rust の利用です。最近、Android と Apple がこの件に関してそれぞれすばらしいブログ投稿を公開しているので、興味がある方はぜひご覧ください。Chrome には数百万行の C++ があり、MiraclePtr などのプロジェクトを通じて C++ の安全性を向上する作業も懸命に行われています。
Posted by Eiji Kitamura - Developer Relations Team