近いうちに、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 は、cxxautocxxbindgencbindgendiplomat、(試験運用版の)crubit などのツールを使ってうまく連携できることがわかっています。ただし、制限事項もあります。やがて新しいツールや改良されたツールによって、こういった制限事項の状況は変わるものと期待できますが、今回の決断や説明は現在のテクノロジーの状況に基づいています。

Chromium が Rust の利用をサポートする方法

Chrome セキュリティ チームは、どのように Rust と C++ のコードの併用にアプローチすべきか、時間をかけて調査してきました。Google のソフトウェア スタックでも、C++ ではなく Rust を記述する時間が増えていることの意味を理解し、安全かつシンプルで信頼性の高い相互利用の制限とはどのようなものかを把握するためです。

この調査に基づき、Chromium の方向性について 2 つの結論を出しました。

  1. 現時点では、C++ から Rust への一方通行の利用のみをサポートします。Chromium は C++ で書かれており、main() から exit() までのスタック フレームの大半は C++ コードです。前述の方向を選んだ理由はここにあります。利用を一方向に限定することで、依存関係ツリーの形を制御します。Rust は C++ に依存することはできないので、依存関係の注入を除けば、Rust が C++ の型や関数を知ることはできません。こうすることで、Rust は任意の C++ コードではなく、C++ から API を通して渡された関数だけを実行できるようになります。
  2. 現時点では、サードパーティ製ライブラリのみをサポートします。サードパーティ製ライブラリはスタンドアロン コンポーネントとして書かれたもので、Chromium の実装についての暗黙的な知識は持ち合わせていません。つまり、シンプルで 1 つのタスクに特化した API を提供しています。言い換えれば、通常はインターフェースが狭く、複雑なポインタグラフや共有所有権は存在しません。C++ で使用するライブラリのレビューし、期待を満たすものであることを確認します。

Chromium での Rust と C++ の相互利用


これまで成功を収めてきたほとんどの C/C++ と Rust の相互利用事例は、狭い API(例 : QUICbluetooth のライブラリ、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 プロジェクトに組み込む対応です。

私たちは、この戦略に従って基準を定め、サードパーティのプロセスを通じて API レビューのレベルを維持する予定です。同時に、相互利用サポートの未来を見据え、Rust と C++ の間で合理的に行えることの限界を押し上げていきます。

その他の関連コンテンツ


メモリが安全でないことは業界全体の問題であり、この領域で変化を起こす戦略の 1 つが Rust の利用です。最近、AndroidApple がこの件に関してそれぞれすばらしいブログ投稿を公開しているので、興味がある方はぜひご覧ください。Chrome には数百万行の C++ があり、MiraclePtr などのプロジェクトを通じて C++ の安全性を向上する作業も懸命に行われています。

Posted by Eiji Kitamura - Developer Relations Team

セキュリティは、いたちごっこのようなものです。攻撃者が新しい手法を生み出せば、ブラウザは一歩先を行くために新たな防御策を講じ続けなければなりません。そのため、Chrome は、サンドボックスとサイト分離をベースとした今までにない強固なマルチプロセス アーキテクチャを構築してきました。これらは、ファジングとともに、現在も私たちの主要な防衛線であり続けています。しかし、それも限界に近づきつつあり、この戦略だけに頼って実際に出回っている攻撃に対処することはできなくなっています。

昨年、Google は、重大なセキュリティ バグの 70% 超がメモリの安全性の問題であることを明らかにしました。つまり、C 言語や C++ 言語のポインタのミスによって、メモリが誤解釈されてしまうのです。

メモリの安全性は、世界のソフトウェア エンジニアリング コミュニティが真剣に受け止める必要がある問題です。しかし、これは同時にチャンスでもあります。なぜなら、多くのバグに同じような根本原因があるということは、1 つの対策で相当のバグを撲滅できる可能性があるからです。

Chrome では、このチャンスを活かすため、大まかに次の 3 つの方法を検討しています。

  1. ポインタが正しいことをコンパイル時にチェックすることで、C++ の安全性を向上
  2. ポインタが正しいことを実行時にチェックすることで、C++ の安全性を向上
  3. メモリ安全な言語をコードベースの一部に使うことについて調査

    「コンパイル時にチェック」とは、Chrome が皆さんのデバイスにインストールされる前の、ビルドプロセスの段階で安全性を保証することを指します。「実行時」とは、Chrome が皆さんのデバイスで実行されている間にチェックを行うことを指します。

    実行時チェックには、パフォーマンスのコストが伴います。ポインタが正しいかどうかをチェックする操作は、メモリや CPU 時間にとっては微少なコストです。しかし、ポインタの数は膨大なので、そのコストは積み重なります。莫大な数のユーザーを持つ Chrome にとって、パフォーマンスは重要です。ユーザーの多くはメモリの少ない低電力モバイル デバイスを使っているため、こういったチェックが増加すれば、ウェブが遅くなってしまいます。

    つまり、選択肢 1、コンパイル時に C++ を安全にする方法を選ぶのが理想です。しかし、この言語はそのような設計にはなっていません。この領域で Google が取り組んできたことを詳しく知りたい方は、借用の問題 : C++ の Borrow-Checker の難しさをご覧ください。

    そのため、ほとんどは選択肢 2 と 3、つまり C++ の安全性を向上させる(ただし遅くなる)か、別の言語を利用する方法をとらざるをえません。Chrome のセキュリティでは、この両方のアプローチを試しています。

    C++ の安全性ソリューションに向けた主な取り組みには、MiraclePtr や ABSL/STL 強化モードなどがあります。どちらも、悪用できるセキュリティ バグの大半を解消することを目指していますが、ある程度のパフォーマンス低下も想定されます。たとえば MiraclePtr は、参照されているメモリを隔離することで解放後の使用に関するバグを防ぎますが、多くのモバイル デバイスではメモリはとても貴重なため、隔離用の領域を割り当てるのは難しくなっています。それでも、MiraclePtr は、ブラウザのプロセスで解放後の使用に関するバグを 50% 以上解消できる可能性を秘めています。今のところ、これは Chrome のセキュリティにとって大きなメリットです。

    それと並行して、将来的に Chrome の一部でメモリ安全な言語を使えないかを検討しています。その第一候補は、Mozilla にいる私たちの友人が開発した Rust です。Rust は(ほとんどが)コンパイル時に安全です。つまり、Rust コンパイラは、コードが皆さんのデバイスにインストールされる前にポインタのミスを見つけます。そのため、パフォーマンスが低下することはありません。しかし、C++ と Rust を十分に連携して使えるかどうかという未解決の問題が残されています。また、たとえ明日から Rust で新しい大型コンポーネントを書き始めたとしても、セキュリティ脆弱性の大部分を解消できるのは、おそらく何年も後になるはずです。さらに、既存のコンポーネントの一部を Rust で書けるほど言語の境界を十分にクリーンにできるかという問題もあります。この点は、まだ明らかではありません。Google は、Chromium ソースコード ツリーのユーザーが触れることのない限られた部分で、Rust の実験を始めています。しかし、製品版の Chrome にはまだ含まれておらず、試験運用版のフェーズにとどまっています。

    以上の理由から、Google は両方の戦略を並行して追求しています。C++ の安全性向上、Chrome での新しい言語の試行というこの領域の最新情報にぜひご注目ください。
    Reviewed by
    Eiji Kitamura - Developer Relations Team

    セキュリティは、いたちごっこのようなものです。攻撃者が新しい手法を生み出せば、ブラウザは一歩先を行くために新たな防御策を講じ続けなければなりません。そのため、Chrome は、サンドボックスサイト分離をベースとした今までにない強固なマルチプロセス アーキテクチャを構築してきました。これらは、ファジングとともに、現在も私たちの主要な防衛線であり続けています。しかし、それも限界に近づきつつあり、この戦略だけに頼って実際に出回っている攻撃に対処することはできなくなっています。

    昨年、Google は、重大なセキュリティ バグの 70% 超がメモリの安全性の問題であることを明らかにしました。つまり、C 言語や C++ 言語のポインタのミスによって、メモリが誤解釈されてしまうのです。

    メモリの安全性は、世界のソフトウェア エンジニアリング コミュニティが真剣に受け止める必要がある問題です。しかし、これは同時にチャンスでもあります。なぜなら、多くのバグに同じような根本原因があるということは、1 つの対策で相当のバグを撲滅できる可能性があるからです。

    Chrome では、このチャンスを活かすため、大まかに次の 3 つの方法を検討しています。

    1. ポインタが正しいことをコンパイル時にチェックすることで、C++ の安全性を向上
    2. ポインタが正しいことを実行時にチェックすることで、C++ の安全性を向上
    3. メモリ安全な言語をコードベースの一部に使うことについて調査

    「コンパイル時にチェック」とは、Chrome が皆さんのデバイスにインストールされる前の、ビルドプロセスの段階で安全性を保証することを指します。「実行時」とは、Chrome が皆さんのデバイスで実行されている間にチェックを行うことを指します。

    実行時チェックには、パフォーマンスのコストが伴います。ポインタが正しいかどうかをチェックする操作は、メモリや CPU 時間にとっては微少なコストです。しかし、ポインタの数は膨大なので、そのコストは積み重なります。莫大な数のユーザーを持つ Chrome にとって、パフォーマンスは重要です。ユーザーの多くはメモリの少ない低電力モバイル デバイスを使っているため、こういったチェックが増加すれば、ウェブが遅くなってしまいます。

    つまり、選択肢 1、コンパイル時に C++ を安全にする方法を選ぶのが理想です。しかし、この言語はそのような設計にはなっていません。この領域で Google が取り組んできたことを詳しく知りたい方は、借用の問題 : C++ の Borrow-Checker の難しさをご覧ください。

    そのため、ほとんどは選択肢 2 と 3、つまり C++ の安全性を向上させる(ただし遅くなる)か、別の言語を利用する方法をとらざるをえません。Chrome のセキュリティでは、この両方のアプローチを試しています。

    C++ の安全性ソリューションに向けた主な取り組みには、MiraclePtrABSL/STL 強化モードなどがあります。どちらも、悪用できるセキュリティ バグの大半を解消することを目指していますが、ある程度のパフォーマンス低下も想定されます。たとえば MiraclePtr は、参照されているメモリを隔離することで解放後の使用に関するバグを防ぎますが、多くのモバイル デバイスではメモリはとても貴重なため、隔離用の領域を割り当てるのは難しくなっています。それでも、MiraclePtr は、ブラウザのプロセスで解放後の使用に関するバグを 50% 以上解消できる可能性を秘めています。今のところ、これは Chrome のセキュリティにとって大きなメリットです。

    それと並行して、将来的に Chrome の一部でメモリ安全な言語を使えないかを検討しています。その第一候補は、Mozilla にいる私たちの友人が開発した Rust です。Rust は(ほとんどが)コンパイル時に安全です。つまり、Rust コンパイラは、コードが皆さんのデバイスにインストールされる前にポインタのミスを見つけます。そのため、パフォーマンスが低下することはありません。しかし、C++ と Rust を十分に連携して使えるかどうかという未解決の問題が残されています。また、たとえ明日から Rust で新しい大型コンポーネントを書き始めたとしても、セキュリティ脆弱性の大部分を解消できるのは、おそらく何年も後になるはずです。さらに、既存のコンポーネントの一部を Rust で書けるほど言語の境界を十分にクリーンにできるかという問題もあります。この点は、まだ明らかではありません。Google は、Chromium ソースコード ツリーのユーザーが触れることのない限られた部分で、Rust の実験を始めています。しかし、製品版の Chrome にはまだ含まれておらず、試験運用版のフェーズにとどまっています。

    以上の理由から、Google は両方の戦略を並行して追求しています。C++ の安全性向上、Chrome での新しい言語の試行というこの領域の最新情報にぜひご注目ください。


    Reviewed by Eiji Kitamura - Developer Relations Team