Skip to content

Instantly share code, notes, and snippets.

@anatawa12
Last active January 12, 2021 04:01
Show Gist options
  • Save anatawa12/8f775a391f86b4b4b2074c76b9bf6036 to your computer and use it in GitHub Desktop.
Save anatawa12/8f775a391f86b4b4b2074c76b9bf6036 to your computer and use it in GitHub Desktop.
Verification Tableについて聞かれたからざっと書いたもの

Verification Tableについて

JVMは誤動作や脆弱性回避のために、コードを実行する前にスタック及びローカル変数の状態を確認します。
例えば

aload_0
istore_1

などと並んでいると(実行前に)エラーが発生します。

しかし、ジャンプ命令があるとこの追跡が難しくなります。
そのため、ジャンプ命令のジャンプ先の位置にスタックおよびローカル変数の状態を保存します。例えば

static Object identity(Object obj) {
    aload 0
    goto label_1
    ; some code here
  label_1:
    areturn
}

とあった場合、label_1:の時点で

local: [java/lang/Object]
stack: [java/lang/Object]

のような状態であることを示す必要があります。

Objectweb ASMにおける表現 上記の指定のための実際のバイナリ上の表現は複雑なため、Objectweb ASMでは比較的シンプルな形で表現されています。

visitFrameの引数について

第2,3と第4,5引数はペアとなり、第3, 5引数は第2, 3引数の数までしか認識されない。また、F_CHOPのように数しか関係ない場合は第3, 5引数が無視される。

第3, 5引数はそれぞれのようそが型を表す プリミティブ型やsuperを呼ぶ前のthis(UNINITIALIZED_THIS)など、Objectではない型はOpcodes内のIntegerインスタンスで表す(ASMのバージョンによってはOpcodes.INTEGERなどと==で比較しているので絶対にvalueOfなどを使用しないこと) new命令からinvokespecialでコンストラクタを呼ぶまでのインスタンスはnew命令を指し示すLabelで表す。 Objectを継承した型はinternalNameのStringで表す

F_NEW こちらは通常、使うことがありません。ClassReaderでクラスファイルを読み込む際、ClassReader.EXPAND_FRAMESを有効にして読み込むと、その他のフレームの代わりにF_NEWが使用されます。 visitFrameの内容

  • stack: 現在のスタックフレーム
  • locak: 現在のローカルフレーム

F_FULL F_NEWと同様に、以前のスタックフレームを無視した新たなフレーム

  • stack: 現在のスタックフレーム
  • locak: 現在のローカルフレーム

F_APPEND 空のスタックと、新たな1~3つのローカル変数が定義されたフレーム

  • stack:無視される(nullも可)
  • local: 1~3つの追加で定義するフレーム

F_CHOP 空のスタックと、1~3つのローカル変数が削除されたフレーム

  • stack: 無視される(nullも可)
  • local: countが1~3つの数、配列は無視される

F_SAME 空のスタックと、前のvisitframeと同一のローカル変数

  • stack: 無視される
  • local: 無視される

F_SAME1 前のvisitframeと同一のローカル変数と、1つのスタックに積まれた値

  • stack: その1つの値
  • local: 無視される

var mv = cw.visitMethod(ACC_STATIC, "method", "(ILjava/lang/Object;)V");
// local: [int, java/lang/Object]
// stack: []

mv.visitFrame(F_SAME1, 
        /* local = */ 0, null, // ignored
        /* stack= */ 1, new Object[] {"java/lang/Object"});

// local: [int, java/lang/Object]
// stack: [java/lang/Object]

mv.visitFrame(F_SAME, 
        /* local = */ 0, null, // ignored
        /* stack= */ 0, null); // ignored

// local: [int, java/lang/Object]
// stack: []

mv.visitFrame(F_APPEND, 
        /* local = */ 2, new Object[] { "java/lang/Object", INTEGER },
        /* stack= */ 0, null); // ignored

// local: [int, java/lang/Object, java/lang/Object, int]
// stack: []

mv.visitFrame(F_CHOP, 
        /* local = */ 1, null, // array was ignored
        /* stack= */ 0, null); // ignored

// local: [int, java/lang/Object, java/lang/Object]
// stack: []

mv.visitFrame(F_FULL, 
        /* local = */ 2, new Object[] { "java/lang/Object", "java/util/List" },
        /* stack= */ 1, new Object[] { INTEGER });

// local: [java/lang/Object, java/util/List]
// stack: [int]

mv.visitFrame(F_CHOP, 
        /* local = */ 1, null,
        /* stack= */ 0, null); // ignored

// local: [java/lang/Object]
// stack: []

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment