ビットコイン・インスティテュート

バリュー・オーバーフロー事件 — ブロック74638で​1,840億BTCが​生成される

2つの巨大な取引出力の合計がオーバーフローして負の値になる様子と、時計アイコン、そして一方の分岐に取り消し線が引かれもう一方が採用チェーンとして続く分岐図を並べたインフォグラフィック

2010年8月15日18:08 UTC頃、ビットコイン開発者ジェフ・ガージックがブロック74638で異常を発見し、BitcoinTalkフォーラムに「Strange block 74638」という新しいトピックを立て、ブロックの生データを貼った:

ジェフ・ガージックの投稿(2010年8月15日18:08 UTC)

「不正なブロック74638 — 92233720368.54277039 BTC?これはUINT64_MAXか?」

ブロック74638の単一トランザクションは、0.5 BTCの入力を使い、92,233,720,368.54277039 BTCの出力を2つ生成した。ブロックのマイナーは50 BTCの報酬に加えて手数料0.51 BTCを受け取っており、これはその入力より0.01 BTC多い。ブロック全体で無から生まれた額は184,467,440,737.09554078 BTCとなり、ビットコインの総発行予定量2,100万BTCの約9,000倍にあたる。ビットコインWikiの184,467,440,737.09551616 BTCはちょうど2⁶⁴ satoshiの値で、ブロック自身の数字から計算した額より2,462 satoshi少ない。

バグの内容:トランザクション検証コードは個々の出力が非負であることを確認していたが、出力の合計における整数オーバーフローをチェックしていなかった。64ビット符号付き整数の最大値(INT64_MAX ≈ 9.2 × 10¹⁸)に近い2つの出力を足すと負の値にオーバーフローし、検証チェックを通過した:0.5 BTC入力 ≥ -0.00997538 BTC出力(オーバーフロー後)。この事件が突いたフィールド、すなわち各出力の8バイトのsatoshi金額は、トランザクション設計ページに記録されている通り、この事件によって強制的に上限が設けられるまでプロトコルレベルの境界を持たなかった。

対応:発見から5時間40分後、サトシはBitcoin version 0.3.10を公開。CheckTransaction()に2つの新しいチェックを追加するソフトフォークだった:

  1. 各出力はMAX_MONEY(21,000,000 BTC)を超えてはならない
  2. すべての出力の合計はMAX_MONEYを超えてはならない

ギャビン・アンドレセンは並行して独自の緊急パッチをテストし、フォーラム参加者knightmbが事前に公開していた清浄なブロックチェーン・スナップショットを復旧の起点として使用した。

サトシは同日中にオーバーフロー修正を取り込んだv0.3.10をリリースし、BitcoinTalkでバージョン0.3.10 - ブロック74638オーバーフローパッチ! として告知し、読者をバグそのものの議論スレッド (topic 823)へ誘導した。

結果:修正チェーンは不正なブロックから約15時間後のブロック74691で無効なチェーンを追い越した。1,840億BTCはブロックチェーンの承認済み履歴から事実上消去された。この事件が体現したのは、緊急ソフトフォークで修正されたチェーンをノードが最大作業量チェーンとして受理していく過程という一般的な仕組みである。これは、コンセンサス設計のエントリで詳しく解説されている。

偶発か悪用か?自然な疑問:これは偶発的に起きたのか、それとも意図的な悪用だったのか。v0.3のソースコードとトランザクション構造を分析すると、一点は確定でき、もう一点は不確定のまま残る。

トランザクション構造から偶発的発生は否定される。オーバーフローを発動させるには、合計がINT64_MAX(約9.2 × 10¹⁸ satoshi)を超える2つの出力が必要で、それぞれ約922億BTC近辺の値でなければならない。この値が通常のウォレット利用で現れる可能性は事実上ない。2010年当時、個人が922億BTCを保有することは不可能で、現実的な残高は最大でも数千BTC程度だった。標準のビットコインウォレットのインターフェースは、出力額を残高とMAX_MONEYで検証してからトランザクションを構築する。92,233,720,368.54277039 BTCという値はタイプミスや丸め誤差で偶然現れる数字ではない。int64の最大値を10⁸ で割った値であり、オーバーフロー直前を狙って意図的に設定しなければ到達しない。ブロック74638のトランザクションを作成するには、生のトランザクションのバイト列を手作業またはカスタムツールで構築し、CheckTransaction() のint64加算でオーバーフローするよう出力値を狙って設定し、署名してブロードキャストする必要があった。いずれの工程も意図的で技術的知識を要する行為である。

実行者と動機は公開記録からは特定できない。ブロックチェーンにはトランザクションと2つの受取アドレスが記録されているが、作成者の身元はオンチェーンデータから復元できない。悪意による攻撃か、概念実証のデモンストレーションか、ストレステストだったかは、コードやトランザクション自体からは判断できない。ブロック74638を採掘したマイナーが悪用に気づいていたかも不明である。バグのある検証ロジックの下では、正直に動作していたノードもそのトランザクションを「有効」と判定するため、包含は共謀を意味しない。

バグと悪用は別カテゴリだが、両方が成立する。CheckTransaction() のint64オーバーフローの存在はバグ(CVE-2010-5139)である。オーバーフローをトリガーするトランザクションの作成とブロードキャストは意図的な悪用行為であり、トランザクション構成の要件からそれは確定できる。その行為が悪意を伴ったかは公開記録からは判定できない。セキュリティ用語では、脆弱性を意図的に突く行為は、実行者の主張する意図に関係なく「攻撃」と呼ぶ。この語自体は悪意を含意しない。したがって本事件はバグ(根底の欠陥)と攻撃(悪用行為)の両方である。

この事件は根本的なパラドックスを露呈した。分散型システムが中央集権的な意思決定によって救われたのだ。緊急対応の中でコミュニティは独立した検証を経ずに修正クライアントを採用し、サトシの判断を信頼した。分散型の設計と、危機対応における単一の権威への依存という矛盾が、このとき初めて可視化された。

本事件は隣接する複数の記録から中核として参照される:構造的パラドックス分析は同じ5時間の対応を分散vs集中の緊張の典型事例として読む。knightmbのスナップショットと伝説分析は復旧パッチが依存した事件直前のブロックチェーンスナップショットを提供した貢献者を扱う。事件直前の文脈はSlashdotビットコイン記事 (2010年7月)とknightmb伝記。Mt. Gox倒産エントリは保管崩壊議論で「プロトコルは生き残った」の初期反例として本事件を扱う。