Bitcoin Core 0.14.0からBitcoin Core 29.0未満のバージョンにおいて、 特別に細工されたブロックを検証する際に、ノードが解法済みメモリにアクセスしてしまう可能性があります。
ブロックの検証中、各トランザクションのインプットの検証に必要なデータは事前計算されキャッシュされます。 特別に細工された不正フロックの場合、このデータがバックグラウンドの検証スレッドからまだアクセスされている最中に破棄されてしまう可能性がありました。 十分なProof of Workを伴うブロックをマイニングできる攻撃者は、これを悪用して被害ノードをクラッシュさせることができた可能性があります。 use-after-freeバグの性質上、このクラッシュがリモートコード実行(RCE)に利用される可能性も否定できませんが、 入力(ブロック)データに対する制約があるため、その可能性は低いと考えられます。
この問題の重大度は 高 と見なされています。
詳細
デフォルトでは、新しいブロックに対するスクリプトの検証は、CScriptCheck関数オブジェクトのvectorを介して
バックグラウンドスレッドにディスパッチされます。各CScriptCheckは、トランザクションの各インプットで必要となるデータを格納する
PrecomputedTransactionDataオブジェクトへのポインタを保持しています。
データそのものではなくポインタを保持しているため、PrecomputedTransactionDataがCScriptCheckよりも
長く生きることを保証する必要があります。
スクリプトチェックのライフタイムは、RAII classであるCCheckQueueControlによって管理されます。
しかし、このコントロールは事前計算されるトランザクションデータよりも先にインスタンス化されます。
C++のローカルオブジェクトは構築された順序とは逆の順序で破棄されるため、
PrecomputedTransactionDataのvectorはCCheckQueueControlよりも前に破棄されます。
ブロックが有効である場合、関数からのリターンの前にCCheckQueueControl::Wait()が呼ばれてから
PrecomputedTransactionDataが破棄されるため、問題は生じません。しかし、
(別のチェックに失敗するなどして)早期リターンが発生した場合、
バックグラウンドのスクリプトスレッドが破棄済みの事前計算トランザクションデータを誤って読み取ってしまう可能性があります。
攻撃者はチェーンの先端における有効なPoWを犠牲にすることで、これを悪用して被害ノードをクラッシュさせることが可能でした。
貢献
本脆弱性はCory Fields (MIT DCI)によって発見され、再現用の概念実証および緩和策の提案を含む詳細なレポートと共に、 責任のある開示が行われました。
タイムライン
- 2024-11-02 Cory Fieldsが非公開でバグを報告
- 2024-11-06 Pieter Wuilleが既に公開されていたPR #31112に、早期リターンを削除することで本問題を回避する秘密裏の修正をプッシュ
- 2024-12-03 PR #31112 がマージされる
- 2025-04-12 Bitcoin Core バージョン 29.0が修正と共にリリースされる
- 2026-04-19 最後の脆弱バージョンであるBitcoin Core バージョン (28.x)がEoLを迎える
- 2026-05-05 開示
