わかるUNIXの歴史 / 番外編 ②信頼のブートストラップ ②

番外①の脅威への、現代の答え ── 種を極小化し、多様性で検める

Bootstrappable Builds ── 種を極小化する Trusting Trust(番外①)は言いました ── 「自分が作っていないバイナリを、信用するしかない」。現代の答えは二つ ── 信用する種を、人間が手で読める大きさ(357バイト)まで小さくする。そして、別系統のコンパイラで二重に検算する。

前提:番外①(Trusting Trust)・第9回(種) 希望:小さく信用し、多様性で確かめる

番外①で、Thompson の暗い双子を見ました ── コンパイラのソースがクリーンでも、汚染バイナリの子孫でビルドする限りトロイは生き延び、信頼は「自分が作っていないもの」で底を打つ、と。では手も足も出ないのか。いいえ。本編の希望(自己ホスティングの輪・手で彫った種)に現代の答えがあったように、この脅威にも、二つの強力な対抗策があります ── ①種を人間可読なサイズまで極小化する(Bootstrappable / Full-Source Bootstrap)②別系統のコンパイラで二重コンパイルして検算する(DDC)。鶏と卵の“希望”の、総仕上げです。

01戦略① ── 信用する種を、手で読める大きさに

番外①の問題の核心は、「信用せざるを得ない最初のバイナリが、大きすぎて中身を検められない」ことでした。ならば ── その種を、ぎりぎりまで小さくすればいい。 Bootstrappable Builds プロジェクトは、信用が必要なバイナリの数と大きさを最小化します。到達点が見事です ── わずか 357 バイトの hex0 という種から、GNU システム全体をソースだけで組み上げる(Full-Source Bootstrap)。

// hex0 ── 357バイトの種。空白区切りの16進を読み、バイトを吐くだけ $ cat hello.hex0 7C 00 00 00 # これはただの16進の並び。人間が目で追える $ ./hex0 hello.hex0 > hello # 16進 → バイト(極小アセンブラ) この 357 バイトだけを信用すればいい。あとは全部ソースから積み上がる

この 357 バイトの上に、少しずつ大きな道具がソースから積み上がります。下の図で、種から gcc、そして GNU システムまで登ってください。第4回「小さな核から全部」・第9回「ブートストラップの階段」の、まっすぐな子孫です。

図1:Full-Source Bootstrap。357バイトの hex0 の種から、hex→M2→Mes→TinyCC→gcc と、各段が次のより大きな道具をソースからビルドし、GNU システム全体まで登る
今回の急所その1 ── 「手で彫った種」が、現代に蘇る

第9回で、最初のバイトは人間が手で彫ったと見ました。Full-Source Bootstrap は、それを現代に取り戻します ── 信用しなければならない種を 357 バイトまで小さくすれば、人間が実際に全部を目で読める。番外①の「信用せざるを得ない、検められないバイナリ」が、監査できる大きさになる。合言葉「最初の卵は、手で彫った」が、セキュリティの実務として生き返るのです。

◇ ◇ ◇

02戦略② ── 別系統のコンパイラで、二重に検める(DDC)

もう一つの答えは、種を小さくするのとは別方向 ── すでにある怪しいコンパイラを、そのまま検証する方法です。David A. Wheeler の Diverse Double-Compiling(DDC, 多様な二重コンパイル)。アイデアはこう ── 疑わしいコンパイラ X とは別系統の、信用できるコンパイラ A を用意し、同じソースを二重にコンパイルして、結果を X のバイナリとビット単位で比べる。

ここが鮮やか。Thompson の毒は、ソースにも、別系統の A にも入っていない(毒は X のバイナリの系譜だけに潜む)。だから A を経由して作り直したバイナリはクリーンで、── もし X が汚染されていれば、作り直した結果と X が食い違い、毒が露見する。下の図で、X を「正直」と「汚染」で切り替えて、汚染のときだけ照合が失敗するのを見てください。

図2:Diverse Double-Compiling。同じソースを、疑わしい X と、別系統の信用できる A で(二重に)コンパイルし、結果を X のバイナリとビット比較。X が汚染されているときだけ食い違う
今回の急所その2 ── ビット同一という審判(第3回の再来)

DDC が成り立つには、同じソースからは同じバイナリが出ること ── 再現ビルド(Reproducible Builds)── が要ります。これは第3回「stage2 == stage3 のビット一致で検算」と、まったく同じ発想。あのとき「正しいコンパイラは何でビルドされても同じ出力」と言いました。DDC はそれを多様性と組み合わせ、「別系統で作り直して一致するなら、バイナリはソースどおり」という審判にしたのです。

03種明かし ── 「全部は作れない」を、こう乗り越える

番外①の教訓は「自分で完全に作ったのでないコードは信用できない」。現実には誰も全部は作れません。でも二つの戦略は、それを実務的に乗り越えます ── 信用する範囲を 357 バイトまで小さくして人間が読み切り、さらに多様な第二コンパイラで相互に検算する。 「全部を作る」の代わりに、「小さく信用し、独立に確かめ合う」。Thompson が「原理的に見破れない」と示した毒は、もはや原理的に見破れるものになりました。

つなぐ声 ── 本編との三重の握手 この番外は、本編と三つで握手します。第3回(stage2==stage3 のビット一致=再現ビルドの祖先)、第4回(小さな核から全部=種の極小化)、第9回(手で彫った種=357バイトの監査可能な種)。鶏と卵をさかのぼる旅で拾った道具立てが、そのままセキュリティの答えになっている ── ブートストラップの構造を深く理解することは、それを守ることに直結していました。
正直な線 ── 万能薬ではない、でも確かな前進

(1) 357バイトの種でも、その hex0動かすカーネルと CPU は信用しています(第10回のハードウェア・マイクロコード)。Full-Source Bootstrap はソフトの種を極小化しますが、ハードの信頼は別問題として残ります。(2) DDC は「別系統の A が独立に汚染されていない(X と共謀しない)」ことを前提にします。完全に独立な A をどう確保するかは、実務上の要です。

(3) 再現ビルド(Reproducible Builds)は大規模な現在進行形の取り組みで、まだ全ソフトが到達しているわけではありません。(4) Guix の Full-Source Bootstrap、GNU Mes、stage0、live-bootstrap は発展途上のプロジェクトで、構成や数値は版により変わります(357バイトは代表的な hex0-seed の例)。要点は「信用する種を小さく・検証可能にし、多様性で確かめる」という方向です。

練習問題(今回の話だけで解けます)
  1. Full-Source Bootstrap は、番外①のどの問題を、どう和らげるか。
    答えを見る
    「信用せざるを得ない最初のバイナリが大きすぎて検められない」問題を、種を357バイトまで極小化して人間が全部読める=監査可能にすることで和らげる。
  2. DDC が Thompson の毒を見破れるのはなぜか。
    答えを見る
    毒はソースにも別系統の信用できるコンパイラ A にも無く、疑わしい X のバイナリ系譜にだけ潜む。だから A で二重コンパイルし直した結果はクリーンで、汚染された X とビット単位で食い違い、露見する。
  3. DDC が前提とする、第3回と同じ性質は何か。
    答えを見る
    再現ビルド(同じソースからビット同一のバイナリが出ること)。第3回の stage2==stage3 のビット一致検算と同じ発想。
  4. 二つの戦略を一言ずつで。共通する考え方は。
    答えを見る
    ①種を人間可読サイズに極小化(監査可能に)②別系統で二重コンパイルして照合(DDC)。共通するのは「全部を自分で作る」代わりに「小さく信用し、独立に確かめ合う」。

まとめ小さく信用し、多様性で確かめる

番外①の脅威(クリーンなソースでも汚染バイナリの子孫なら毒が残る/信頼は作っていないもので底を打つ)に、現代は二つで応える。①Bootstrappable / Full-Source Bootstrap:信用する種を hex0357バイトまで極小化し、そこから hex→M2→Mes→TinyCC→gcc→GNU システムをソースだけで積み上げる。種が人間可読なら、番外①の「検められないバイナリ」は監査できる②Diverse Double-Compiling(DDC, Wheeler):別系統の信用できるコンパイラで二重コンパイルし、結果を疑わしいバイナリとビット比較。毒はソースにも別系統にも無いので、汚染だけが食い違って露見する。

DDC の土台は再現ビルド=第3回「stage2==stage3 のビット一致」の子孫だ。この番外は本編と三重に握手する ── 第3回(ビット一致)、第4回(小さな核から全部)、第9回(手で彫った種)。ブートストラップの構造を深く知ることが、そのまま守る力になった。Thompson が「原理的に見破れない」と示した毒は、小さく信用し、多様性で確かめることで、見破れるものになった。── 鶏と卵の希望は、脅威にも耐える。

この文書は「わかるUNIXの歴史」番外編②です。史実・技術:Bootstrappable Builds プロジェクトは、信用が必要なバイナリ(種)の数と大きさの最小化を目指す。GNU Guix の Full-Source Bootstrap(2023)では、約357バイトの hex0-seed バイナリから stage0-posix が hex0→hex1→catm→hex2→M0→cc_x86→M1→M2→get_machine(mescc-tools)→M2-Planet を構築し、続いて GNU Mes(Scheme インタプリタ+C コンパイラ MesCC)を経て、軽微なパッチを当てた自己ホスティングの TinyCC、さらに従来型の C ベースのブートストラップ(gcc など)へと積み上げる。hex0 は空白区切りの16進を読みバイトを出力する極小アセンブラ。Diverse Double-Compiling(DDC)は David A. Wheeler による技法で、コンパイラのソースを別系統の信用できるコンパイラで二重にコンパイルし、その結果を検査対象のバイナリとビット単位で比較して、一致すればバイナリがソースに対応することを(一定の前提の下で)示す。2005年 ACSAC 論文(非形式的)、2009年博士論文(形式的証明)。DDC の前提として再現ビルド(Reproducible Builds、同一ソースからビット同一のバイナリ)が重要。数値・構成はプロジェクトの版により変化しうる。ハードウェア/マイクロコード(第10回)の信頼、独立な第二コンパイラの確保などは別途の課題。本稿は防御・教育目的の解説。 ── 印刷する場合はブラウザの「印刷」から「PDF に保存」を(印刷版では図の操作・解答は静止/非表示になります)。

印刷 / PDF 化:⌘+P(Windows は Ctrl+P)。画面では図1のスライダーで357バイトの種から GNU システムまでの積み上げを、図2のボタンで DDC(X が汚染のときだけ照合失敗)を試せます。「答えを見る」で解答が開きます。