GNU Cコンパイラはいかにして暗号技術のClippyになったのか

FOSDEM 2026 セキュリティソフトウェアの開発者たちは、私たちを守ろうとする中で思いがけない敵に遭遇している。現代のコンパイラだ。

今日のコンパイラはコードを最も効率的な形にまで煮詰めるが、その過程で安全対策を台無しにしてしまうことがある。

「現代のソフトウェアコンパイラが私たちのコードを壊している」と、René Meuselは述べ、2月1日に行われたFOSDEMの講演で懸念を共有した。

MeuselはBotan暗号ライブラリを管理しており、Rohde & Schwarz Cybersecurityのシニアソフトウェアエンジニアでもある。

BotanのメンテナとしてMeuselは、暗号化が破られ得るさまざまな方法を把握している。数学を正しくするだけでは不十分だ。ソフトウェアもまた、安全に暗号化・復号できなければならない。

この作業を実行するコードを書くのは、想像する以上に厄介になり得る。そしてコンパイラは助けになっていない。

サイドチャネルを塞ぐ

Meuselは、単純なログインシステムを実装する際に直面する問題の例を示した。

ユーザーがパスワードを入力し、それがデータベースと1文字ずつ照合される。最初の文字が一致しなかった時点でエラーメッセージが返される。

侵入を試みる観察者にとっては、そのエラーが返るまでの時間が、推測したパスワードのうち何文字がすでに正しく入力されているかを示してしまう。応答時間が長いほど、より多くの文字が当たっていることになる。

このサイドチャネル漏えいは過去に、総当たりによる侵入を容易にするために利用されてきた。必要なのは、応答時間のごくわずかな差を見分けられる高分解能の時計だけだ。

幸い暗号屋は生来の偏執的な人種だ。彼らはすでに、ユーザーに対する応答時間を均一化して情報が漏れにくくする予防関数を作っている。こうした定数時間実装は「実行時間をパスワードから独立させる」とMeuselは言う。

問題解決? コンパイラの思い通りならそうはいかない

GNU Cコンパイラはブール値について推論するのが非常に得意だ。賢すぎるのかもしれない。MicrosoftのClippy級に賢い、というわけだ。 

Meuselは定数時間実装をGCC 15.2(-std=c++23 -O3)に通した。

文字をチェックするループは、文字が正しいと早期に抜けるため、GCCは関数の残りは不要だと判断した。しかし、実際にタイミングを補正していた残りのコードが投げ捨てられ、サイドチャネル脆弱性が再び露呈した。ありがとう、GCC。

Meuselは、なぜCの最適化がブール比較を目の敵にするのかまでは踏み込まなかったが、優れたCプログラマはブール論理の攻撃的な最適化を恐れるべきだと知っている。これは危険になり得るからだ。 

ブールの判断は分岐を意味し、これはハードウェアにとって高コストなので、コンパイラとしてはむしろ分岐だらけのコードを分岐なしの制御フロー論理に変換したい。まあ、それでいいよね? 

それ、ブール論理ですよね? 

コツは、この小さなプログラムの意味論をコンパイラから隠すことだ、とMeuselは助言した。 

第一歩は、ループに渡しているブール値を2ビット整数に置き換え、ビットシフトやビット演算で入力をマスクすることだ(Meuselは講演で必要なコードを提示しているので、オタク的な旨味を全部味わいたいならスライドを参照してほしい)。

これで片が付くと思うだろう。 

だがGCCはそれ以上に賢い。こっそりブール比較をしようとしていることを見抜いてしまう。

そこで入力と出力の両方に難読化関数を適用する必要がある。ただしプログラム自体の利益のためではなく、単にそれらがコンパイラが「私たちをひどい目に遭わせる」ために使い得る別の値だからだ、とMeuselは言う。 

そして最後に、値をインラインアセンブリコードに通す必要がある。そのコードは何もしないで同じ値を返すだけだ。要するに、アセンブリを理解しないコンパイラに対し、どれほどブールっぽく見えようともこれらの値に手を出すな、と警告する効果がある。

「今どきは、こうやってやるんです」と彼は付け加えた。 

暗号実装者の宿命

「ここであなたは『うーん、なんだか込み入ってきて、間違えやすくなってきたな』と言うかもしれない……そしてそれは間違っていない」とMeuselは言った。

平均的なプログラマに、インラインアセンブリや、こうした本質的に難解な難読化技法の理解を求めるのは公平だろうか? ましてや将来このソフトウェアを保守する人たちにとっては? そしてAIがこうしたごまかしの数々をくぐり抜けるのに、あと何トークンかかるのだろう?

しかしこれが暗号ライブラリ実装者の宿命だ。容赦ないコンパイラから解決策を隠しつつ、サイドチャネル攻撃を補強する方法を見つけなければならない。

他のコンパイラにも、きっと独自の癖がある。Intel C++ CompilerやClangにどんな恐怖が潜んでいるかは誰にも分からない。しかもコンパイラの世代が新しくなるたびに、対処すべき最適化の新セットが現れるのだ、とMeuselは嘆いた。 

地形を知り、群れで行動せよ

Meuselの講演から得られる教訓はいくつもあるが、最大のものはこうだ。GCCでは最適化ボタンをオフにしてしまうべきなのでは?

それでも、コンパイラ開発者はコード効率以外の要因も考慮したほうがよいのかもしれない。

「彼らはあなたのコードを速くしたいし、それが本当に得意です。でも、実装に求められる他の質的要件を、この検討に入れていないのです」とMeuselは言った。

ある聴衆が示唆したように、いつかコンパイラが、コードのどの領域をいじらないかを指定するプロンプトを受け付けるようになるかもしれない。 

それまでは、セキュリティソフトウェアの設計者は、使用する開発ツールやその動作も含め、設計しているシステムのあらゆる部分を念頭に置く必要がある。 

そのためにMeuselは、オープンソースのメモリデバッグツールvalgrindを勧めた。たとえばプログラムが未定義値に依存している場合に警告してくれる。 

最後に、セキュリティの実装は難しい。数学が絡むからだけではない。ひとりでやるには難しすぎる。自作するな、代わりにプロジェクトに参加せよ、とMeuselは助言した。 ®

翻訳元: https://go.theregister.com/feed/www.theregister.com/2026/02/09/compilers_undermine_encryption/

ソース: go.theregister.com