はじめに

初めまして。 『DApps開発入門』という本や色々記事を書いているかるでねです。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

今回は「EIP2327」についてまとめていきます。

EIP2327は、コントラクトのバイトコード末尾へ置いたデータを、実行する命令列から区切るBEGINDATA命令の提案です。 後続バイトは読み出せますが、実行もジャンプもできません。

概要

コントラクトのバイトコードには、実行する命令だけでなく、定数、コンパイラのメタデータ、初期化処理で扱うランタイムコードを直接入れられます。 従来のEVMは後続バイトをデータとして区別しないため、解析器は末尾まで命令列として扱います。

EIP2327は、バイト値0xb6へBEGINDATAを割り当てます。 最初のBEGINDATAより後ろをデータ領域とし、実行と有効なJUMPDESTの探索から外します。

BEGINDATAで実行コードとデータ領域を分ける

境界で変わるのは、データの保存先ではなく、EVMが後続バイトをどう解釈するかです。 データ領域はコードの一部として残り、CODECOPYとEXTCODECOPYで読み出せます。

動機

データ領域に0x5bが含まれていても、開発者がその位置をジャンプ先として使うつもりとは限りません。 しかし境界がなければ、逆アセンブラ、チェーンエクスプローラー、静的解析器は、その値を命令列の一部として最後まで読みます。

有効なJUMPDESTを求める処理では、実行されないデータまで解析対象になります。 そのため、表示上は意味のない命令列が並び、制御フロー検証にも不要なバイト列を含めます。

データ領域を含める解析と境界で止める解析

最初のBEGINDATAで解析を止めると、データ領域を制御フローの候補へ混ぜません。 提案は、他チェーンのトランザクションをオンチェーンで検証する時に、不要なジャンプ先解析を減らす用途も挙げています。

仕様

EIP2327は、最初に現れたBEGINDATAだけを実行コードとデータの境界にします。 境界位置と、その後ろのすべての位置は、有効なジャンプ先になりません。

最初のBEGINDATAでJUMPDESTの探索を止める

BEGINDATA

js
BEGINDATA // 0xb6

BEGINDATAは、実行可能な命令列の終端を示す命令です。 有効なJUMPDESTを計算する処理は、最初のBEGINDATAに到達した位置で停止します。

  1. バイトコードを先頭から読む

    解析器は、BEGINDATAに達するまで命令を読み、有効なJUMPDESTを記録します。

  2. 最初の境界で探索を止める

    BEGINDATAと同じ位置、または後ろの位置は、有効なJUMPDESTへ追加しません。

  3. 実行時は呼び出しフレームを止める

    実行がBEGINDATAへ到達すると、現在の呼び出しフレームはSTOPと同じ意味で終了します。

  • 実行条件

  • BEGINDATAへジャンプすると、BAD_JUMP_DESTINATIONエラーになります。

  • BEGINDATAのガスコストは0です。

実行がBEGINDATAへ到達した時の停止位置

バイトコード中に0xb6が複数あっても、解析を止めるのは最初の位置だけです。 後続バイトは命令として解釈しないため、2個目以降の0xb6へ特別な意味はありません。

データ領域に残る性質

BEGINDATAは後続データを消去せず、実行しない領域として区切ります。 コピー命令とコードサイズの扱いは変えません。

読み出せるが実行できないデータ領域

  • CODECOPYとEXTCODECOPYは、データ領域を含む後続バイトを読み出せます。
  • CODESIZEとEXTCODESIZEは、データ領域を含むコード全体のサイズを返します。
  • データ領域にある0x5bは、有効なJUMPDESTになりません。

補足

BEGINDATAは、サブルーチンと静的ジャンプを扱うEIP615で、ジャンプテーブルの位置を見分ける案として出ていました。 EIP2327はこの命令を単独で定義し、コードとデータを分ける規則へ絞っています。

BEGINDATAを単独のコードとデータの境界として扱う

EIP2327のステータスはStagnantです。 Ethereumへ採用されていないため、現行EVMの命令やSolidityの出力としてBEGINDATAを使える前提にはしません。

テストケース

提案は、解析段階と実行段階を別々に確認します。

ジャンプ先の拒否と到達時の停止を分けて検証する

  1. データ領域内の0x5bへジャンプする

    BEGINDATAより後ろにある0x5bへジャンプすると、BAD_JUMP_DESTINATIONエラーになります。

  2. 実行がBEGINDATAへ到達する

    実行中にBEGINDATAへ到達すると、現在の呼び出しフレームが停止します。

コピー命令で後続データを読めることと、コードサイズが後続データを含むことも別に確認します。 ジャンプ先の拒否だけでは、読み出し規則とサイズ規則の確認になりません。

互換性

影響を受けるのは、未使用だった0xb6をデータとして置き、その位置を実行時に飛び越えて後続の命令へ進むバイトコードです。 Solidityコンパイラはその構造を生成していないため、通常のコンパイラ出力はこの変更に依存しません。

通常の出力と0xb6を飛び越える特殊なバイトコード

手書きのバイトコードや特殊な生成器を使う場合は、0xb6の位置とジャンプ先を確認します。 最初の0xb6を飛び越える構造は、BEGINDATAとして解釈される環境では動作しません。

参考実装

提案には参照実装がありません。 実装する場合は、JUMPDESTの解析停止、到達時のSTOP、コピー命令、コードサイズを別々に検証します。

参照実装がない場合に確認するEVM処理

CODECOPYとEXTCODECOPYでデータを読み出せることと、CODESIZEとEXTCODESIZEの結果が変わらないことを、ジャンプ先解析とは別のテストにします。

セキュリティ

データ領域に偶然含まれる0x5bを有効なJUMPDESTと誤認しないことは、制御フローを検証する前提です。 最初のBEGINDATAで解析を止め、境界位置そのものへのジャンプも拒否します。

実行対象から外れてもデータは公開されたまま残る

実行できないことは、データを秘匿することではありません。 データ領域はCODECOPYとEXTCODECOPYで読めるため、秘密鍵や非公開の入力を置く場所には使えません。

最後に

今回は「EIP2327」についてまとめてきました。

EIP2327は、後続バイトをデータとして残したまま、実行とジャンプ先解析から外す提案です。 実装する側は、最初の境界で解析を止めること、到達時に停止すること、後続データを読み出せることを別々に確認します。 提案はStagnantであり、現行Ethereumで利用できる命令ではありません。

他でも色々記事を書いているのでぜひよろしければ読んでいってください!