Mozilla Hacksの「Option Soup: the subtle pitfalls of combining compiler flags」を読みました。Firefox 120 Betaで発生したLinux向けクラッシュを入口に、配布パッケージのビルド設定、C++標準ライブラリ、ELFのシンボル解決まで掘っていく技術調査の記事です。
記事の要点
最初に見えていた現象は、WebGLページを開いたときにstd::locale::operator=周辺でクラッシュするというものでした。一見するとWebGLやANGLEの問題に見えますが、問題のコードはstd::ostringstreamにstd::locale::classic()を設定するだけの普通のC++コードです。
調査を進めると、Ubuntu 18.04 LTS向けのFirefox Betaパッケージでlibstdc++が静的リンクされていることが分かります。古い環境に新しいC++ランタイムを持ち込むための現実的な工夫でしたが、Firefox側では-Bsymbolic-functionsも使われていました。この組み合わせにより、関数シンボルと変数シンボルの解決のされ方がずれ、std::localeの初期化状態がモジュール間で食い違う状況が生まれた、というのが記事の核心です。
参照先も確認したこと
元記事から、Bugzillaのクラッシュ報告、Firefox Beta PPA、Ubuntu向けビルドログ、Launchpadの関連バグ、GCC/libstdc++側の議論、NixOS側のWebGLクラッシュ報告、NixOSの関連PR、Firefox configureへの検出追加を確認しました。
Bugzilla側でも、クラッシュはFirefox Build Systemの問題として扱われ、Firefox 120以降で修正対象になっています。NixOS側の報告でも、Firefox 120でWebGLを使うと落ちる再現手順が共有されており、Mozilla側の調査結果がディストリビューション側の修正にもつながったことが分かります。
気づき
今回の気づきは、ビルドフラグは個別には妥当に見えても、組み合わせるとランタイムの前提を壊すことがあるという点です。-static-libstdc++は古いOS向けに新しいC++機能を届けるための手段で、-Bsymbolic-functionsもシンボル解決を制御するための手段です。しかし、それぞれの意図が正しくても、標準ライブラリの内部状態を複数モジュールがどう共有するかという低いレイヤーで矛盾が出ることがあります。
この話は、Firefoxのような巨大プロジェクトだけの特殊事例ではありません。OS配布、独自ビルド、古い実行環境への対応、静的リンク、最適化フラグの追加は、多くのプロダクトでも起こり得ます。クラッシュがアプリケーションコードの近くに見えても、原因はビルド設定やリンク時の挙動にあるかもしれない、という調査の視野を持つことが大事だと感じました。
記事タイトルの「Option Soup」はかなり的確です。オプションを足していくほど解決できる問題も増えますが、そのぶん組み合わせの意味を説明できない構成にも近づきます。最終的にFirefox側では、この特定の危険な構成をconfigureで検出する対策が追加されており、再発防止まで含めた読みごたえのある調査記録でした。

コメントを残す