公開日: 2025 年 3 月 19 日
Skrifa は Rust で記述されており、すべてのユーザーにとって Chrome でのフォント処理を安全に行うために FreeType の代替として作成されました。Skifra は Rust のメモリ安全性を利用し、Chrome のフォント技術の改善を迅速に反復できます。FreeType から Skrifa に移行することで、フォントコードを変更する際にアジャイルかつ大胆な対応が可能になります。セキュリティ バグの修正に費やす時間が大幅に短縮され、アップデートの迅速化とコード品質の向上につながっています。
この投稿では、Chrome が FreeType から移行した理由と、この移行によって実現した改善点について説明します。
FreeType を置き換える理由
ウェブは、ユーザーがさまざまな信頼できないソースから信頼できないリソースを取得し、正常に動作し、安全に動作することを期待できることが特徴です。この前提は一般的に正しいものですが、ユーザーに対してその約束を守るにはコストがかかります。たとえば、ウェブフォント(ネットワーク経由で配信されるフォント)を安全に使用するために、Chrome では次のようなセキュリティ対策が講じられています。
- フォント処理は、2 つのルールに従ってサンドボックス化されます。フォントは信頼できないものであり、使用するコードは安全ではありません。
- フォントは、処理前に OpenType Sanitizer に渡されます。
- フォントのデコンプレッションと処理に関連するすべてのライブラリはファジング テストされています。
Chrome には FreeType が付属しており、Android、ChromeOS、Linux では FreeType がメインのフォント処理ライブラリとして使用されます。つまり、FreeType に脆弱性があると、多くのユーザーが影響を受けます。
FreeType ライブラリは、指標の計算とフォントからのヒント付きアウトラインの読み込みに Chrome で使用されます。全体的に、FreeType の使用は Google にとって大きなメリットとなっています。複雑な処理を適切に実行し、Google は広範囲にわたって依存し、貢献しています。ただし、これは安全でないコードで記述されており、悪意のある入力の可能性が低かった時代に作られたものです。ファジングによって見つかった問題の処理だけで、Google は少なくとも 0.25 人のフルタイム ソフトウェア エンジニアを必要としています。さらに悪いことに、コードがユーザーに配布された後にしか見つからない、または見つからないこともあります。
この問題のパターンは FreeType に固有のものではありません。Google は、見つけられる限り優秀なソフトウェア エンジニアを採用し、すべての変更をコードレビューし、テストを義務付けているにもかかわらず、他の安全でないライブラリで問題が発生していることを確認しています。
問題が絶えず発生する理由
FreeType のセキュリティを評価した結果、主に 3 つの問題クラスが発生することが判明しました(ただし、これらに限定されません)。
安全でない言語の使用
パターン/問題 | 例 |
---|---|
手動メモリ管理 |
|
未チェックの配列アクセス | CVE-2022-27404 |
整数オーバーフロー | 埋め込み仮想マシンの実行中に、CFF 描画とヒントの TrueType ヒンティングが行われる https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
ゼロ化とゼロ化以外の割り当ての不適切な使用 | https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94 のディスカッション、その後 8 件の難読化ツールの問題が見つかりました |
無効なキャスト | マクロの使用については、次の行を参照 |
プロジェクト固有の問題
パターン/問題 | 例 |
---|---|
マクロにより明示的なサイズ型指定が不明瞭になる |
|
新しいコードは、防御的に記述しても、バグが常に追加されます。 |
|
テスト不足 |
|
依存関係に関する問題
ファジングにより、FreeType が依存するライブラリ(bzip2、libpng、zlib など)で問題が繰り返し特定されています。たとえば、freetype_bdf_fuzzer: inflate での未初期化値の使用と比較してください。
ファジングだけでは不十分
ファジング(無効な入力をランダムに含む幅広い入力を使用した自動テスト)は、Chrome の安定版リリースに含まれる多くの種類の問題を検出することを目的としています。Google は、Google の oss-fuzz プロジェクトの一環として FreeType をファジングしています。問題は検出されますが、フォントは次の理由でファジングに比較的耐性があることが実証されています。
フォントファイルは、さまざまな種類の情報が含まれているため、動画ファイルに匹敵する複雑なファイルです。フォント ファイルは複数のテーブルのコンテナ形式です。各テーブルは、テキストとフォントを一緒に処理して画面上に正しく配置されたグリフを生成するために、異なる目的を果たします。フォント ファイルには次のものが含まれています。
- 可変フォント用のフォント名やパラメータなどの静的メタデータ。
- Unicode 文字からグリフへのマッピング。
- グリフの画面レイアウトに関する複雑なルールセットと文法。
- 視覚情報: 画面に配置されたグリフの外観を説明するグリフの形状と画像情報。
- ビジュアル テーブルには、グリフの形状を変更するために実行されるミニプログラムである TrueType ヒンティング プログラムを含めることができます。
- CFF または CFF2 テーブルの文字列。これは、CFF レンダリング エンジンで実行される強制的なカーブ描画とヒントの指示です。
フォント ファイルには、独自のプログラミング言語と状態マシン処理と同等の複雑さがあり、特定の仮想マシンで実行する必要があります。
形式が複雑なため、フォント ファイルの問題を見つける際にファジングには欠点があります。
次の理由により、優れたコードカバレッジやファジング プログレスを達成することは困難です。
- 単純なビット反転/シフト/挿入/削除スタイルのミュータタを使用して TrueType ヒントプログラム、CFF 文字列、OpenType レイアウトをファジングすると、すべての状態の組み合わせに到達するのが困難です。
- ファジングでは、少なくとも部分的に有効な構造を生成する必要があります。ランダムな突然変異ではほとんど発生しないため、特にコードの深いレベルでは、十分なカバレッジを達成することが困難になります。
- ClusterFuzz と oss-fuzz の現在のファジングでは、構造認識型ミューテーションはまだ使用されていません。文法や構造に依存するミューテータを使用すると、早期に拒否されるバリアントの生成を回避できますが、開発に時間がかかり、検索空間の一部が欠落する可能性があります。
ファジングを進めるには、複数のテーブルのデータが同期されている必要があります。
- ファジングツールの通常のミューテーション パターンでは、部分的に有効なデータが生成されないため、多くの反復処理が拒否され、進行が遅くなります。
- グリフ マッピング、OpenType レイアウト テーブル、グリフ描画は相互に接続され、相互に依存しているため、組み合わせ空間を形成し、その隅にファジングで到達するのは困難です。
- たとえば、重大度の tt_face_get_paintCOLRv1 脆弱性を見つけるのに 10 か月以上かかりました。
Google は最大限の努力を払っていますが、フォント セキュリティの問題がエンドユーザーに繰り返し報告されています。FreeType を Rust の代替手段に置き換えることで、複数のクラスの脆弱性を防ぐことができます。
Chrome の Skrifa
Skia は、Chrome で使用されるグラフィックス ライブラリです。Skia は FreeType を使用して、フォントからメタデータと文字形を読み込みます。Skrifa は Rust ライブラリであり、Fontations ライブラリ ファミリーの一部です。Skia で使用される FreeType の部分を安全に置き換えることができます。
FreeType を Skia に移行するため、Chrome チームは Skrifa に基づいて新しい Skia フォント バックエンドを開発し、変更を段階的にユーザーにロールアウトしました。
- Chrome 128(2024 年 8 月)では、安全な試験運用として、カラーフォントや CFF2 など、あまり使用されていないフォント形式で使用できるように Fontations を有効にしました。
- Chrome 133(2025 年 2 月)で、Linux、Android、ChromeOS でのすべてのウェブフォント使用と、Windows と Mac でのフォールバックとしてウェブフォント使用(システムがフォント形式をサポートしていないが Chrome で表示する必要がある場合)で Fontations を有効にしました。
Chrome への統合では、Chrome セキュリティ チームが導入したコードベースに Rust をスムーズに統合しています。
今後、オペレーティング システムのフォントも Fontations に切り替える予定です。まず Linux と ChromeOS で、その後 Android で切り替える予定です。
安全第一
主な目標は、メモリへの範囲外のアクセスによって発生するセキュリティの脆弱性を軽減すること(理想的には排除すること)です。Rust では、安全でないコードブロックを回避すれば、この機能がすぐに利用できます。
パフォーマンス目標を達成するには、現在は安全でない 1 つのオペレーション(任意のバイト値を厳密に型付けされたデータ構造として再解釈する)を実行する必要があります。これにより、不要なコピーを実行せずにフォント ファイルからデータを読み取ることができ、高速なフォント パーサーの生成に不可欠です。
安全でないコードを回避するため、この責任を bytemuck にアウトソースすることにしました。これは、この目的のために特別に設計された Rust ライブラリであり、エコシステム全体で広くテストされ、使用されています。未加工データの再解釈を bytemuck に集中させることで、この機能を 1 か所にまとめ、監査し、その目的で安全でないコードを繰り返すことを回避できます。安全な変換プロジェクトは、この機能を Rust コンパイラに直接組み込むことを目的としています。この機能が利用可能になり次第、切り替えを行います。
正確性が重要
Skrifa は独立したコンポーネントで構築されており、ほとんどのデータ構造は不変になるように設計されています。これにより、読みやすさ、保守性、マルチスレッド化が向上します。また、コードを単体テストしやすくなります。Google はこの機会を利用して、低レベルの解析ルーティンから高レベルのヒント仮想マシンまで、フルスタックを網羅する約 700 個の単体テストスイートを作成しました。
正確性には忠実性も含まれます。FreeType は高品質のアウトラインの生成で高く評価されています。適切な交換品とするには、この品質を一致させる必要があります。そのために、さまざまな構成のフォント ファイルのバッチについて、Skrifa と FreeType の出力を比較する専用のツール fauntlet を構築しました。これにより、品質の低下を回避できるという保証が得られます。
また、Chromium に統合する前に、Skia でさまざまなピクセル比較を行い、FreeType レンダリングと Skrifa レンダリング、Skia レンダリングを比較して、必要なすべてのレンダリング モード(さまざまなアンチエイリアシング モードとヒントモード)でピクセル差が絶対に最小限になるようにしました。
ファズ テストは、ソフトウェアが不正な形式や悪意のある入力にどのように反応するかを判断するための重要なツールです。Google は 2024 年 6 月以降、新しいコードを継続的にファジングしてきました。これには、Rust ライブラリ自体と統合コードが含まれます。ファジングツールは(この記事の執筆時点で)39 個のバグを検出しましたが、これらのバグはいずれもセキュリティ上重大なものではありません。望ましくない視覚的な結果や、制御されたクラッシュを引き起こす可能性がありますが、悪用可能な脆弱性にはつながりません。
次へ!
テキストに Rust を使用した取り組みの結果に非常に満足しています。より安全なコードをユーザーに提供し、デベロッパーの生産性を高めることは、Google にとって大きなメリットです。Google は、テキスト スタックで Rust を使用する機会を今後も探していく予定です。詳しくは、Oxidize で Google Fonts の今後の計画をご覧ください。