【问题标题】:java.lang.VerifyError: Bad local variable type after bytecode instrumentationjava.lang.VerifyError:字节码检测后局部变量类型错误
【发布时间】:2014-12-15 15:27:24
【问题描述】:

我检测了一些 Java 字节码。我尝试做的是:

InstrumentStackElem[] stack;
int stackpointer;
void foo(){
    stackpointer = (stackpointer + 1) % stack.length;
    InstrumentStackElem localref = stack[stackpointer];
    localref.init();

    //before each return
    localref.clear();
    stackpointer = (stackpointer -1 +stack.length) % stack.length;

因此,在每个方法的开头,我都会递增堆栈指针并获取对数组元素的本地引用。在每次返回之前,我在元素上调用一个 clear 方法并再次减少计数器。

当我尝试访问调用 localref.clear() 的字段时,我总是收到 java.lang.VerifyError。

示例方法:

public static boolean isValidName(final String s) {
        if (!s.matches("[_a-zA-Z][_a-zA-Z0-9]*")) {
            return false;
        }

        if (s.toCharArray()[0] == '_') { // this check should not be -> this is
                                            // a bug
            return false;
        }

        return true;
    }

相应的检测字节码

 public static boolean isValidName(java.lang.String);
    Code:
       0: getstatic     #38                 // Field instrumentation_static_stackpointer:I
       3: iconst_1
       4: iadd
       5: bipush        16
       7: irem
       8: putstatic     #38                 // Field instrumentation_static_stackpointer:I
      11: getstatic     #40                 // Field instrumentation_static_stack:[Lme/instrumentor/InstrumentStackElem;
      14: getstatic     #38                 // Field instrumentation_static_stackpointer:I
      17: aaload
      18: astore_2
      19: aload_2
      20: bipush        -1
      22: ldc           #113                // String isValidName_Ljava_lang_String__Z
      24: invokevirtual #48                 // Method me/instrumentor/InstrumentStackElem.init:(ILjava/lang/String;)V
      27: aload_0
      28: ldc           #115                // String [_a-zA-Z][_a-zA-Z0-9]*
      30: invokevirtual #118                // Method java/lang/String.matches:(Ljava/lang/String;)Z
      33: ifne          56
      36: iconst_0
      37: aload_2
      38: invokevirtual #110                // Method me/instrumentor/InstrumentStackElem.clear:()V
      41: getstatic     #38                 // Field instrumentation_static_stackpointer:I
      44: iconst_1
      45: isub
      46: bipush        16
      48: iadd
      49: bipush        16
      51: irem
      52: putstatic     #38                 // Field instrumentation_static_stackpointer:I
      55: ireturn
      56: aload_0
      57: invokevirtual #122                // Method java/lang/String.toCharArray:()[C
      60: iconst_0
      61: caload
      62: bipush        95
      64: if_icmpne     87
      67: iconst_0
      68: aload_2
      69: invokevirtual #110                // Method me/instrumentor/InstrumentStackElem.clear:()V
      72: getstatic     #38                 // Field instrumentation_static_stackpointer:I
      75: iconst_1
      76: isub
      77: bipush        16
      79: iadd
      80: bipush        16
      82: irem
      83: putstatic     #38                 // Field instrumentation_static_stackpointer:I
      86: ireturn
      87: iconst_1
      88: aload_2
      89: invokevirtual #110                // Method me/instrumentor/InstrumentStackElem.clear:()V
      92: getstatic     #38                 // Field instrumentation_static_stackpointer:I
      95: iconst_1
      96: isub
      97: bipush        16
      99: iadd
     100: bipush        16
     102: irem
     103: putstatic     #38                 // Field instrumentation_static_stackpointer:I
     106: ireturn

这是引发的确切错误:

  Unexpected error: Lexer: java.lang.VerifyError: Bad local variable type
Exception Details:
  Location:
    lexer/Lexer.isValidName(Ljava/lang/String;)Z @68: aload_2
  Reason:
    Type top (current frame, locals[2]) is not assignable to reference type
  Current Frame:
    bci: @68
    flags: { }
    locals: { 'java/lang/String' }
    stack: { integer }
  Bytecode:
    0000000: b200 2604 6010 1070 b300 26b2 0028 b200
    0000010: 2632 4d2c 10ff 1271 b600 302a 1273 b600
    0000020: 769a 0017 032c b600 6eb2 0026 0464 1010
    0000030: 6010 1070 b300 26ac 2ab6 007a 0334 105f
    0000040: a000 1703 2cb6 006e b200 2604 6410 1060
    0000050: 1010 70b3 0026 ac04 2cb6 006e b200 2604
    0000060: 6410 1060 1010 70b3 0026 ac            
  Stackmap Table:
    same_frame(@56)
    same_frame(@87)

我使用 asm 库来检测方法并使用 this.method.visitLocalVariable("localstackref", "[Lme/instrumentor/InstrumentStackElem;",null, bl, el,this.stackelementindex); 添加局部变量。 stackelementindex 在这种情况下是 2。

有人可以帮帮我吗?

【问题讨论】:

    标签: java bytecode java-bytecode-asm


    【解决方案1】:

    生成的字节码是正确的,除了您忘记更新堆栈映射表。

    作为参考,这里是您生成的字节码的伪代码。

    instrumentation_static_stackpointer = (instrumentation_static_stackpointer + 1) % 16
    a2 = instrumentation_static_stack[instrumentation_static_stackpointer]
    a2.init(-1, "isValidName_Ljava_lang_String__Z")
    if (!s.matches("[_a-zA-Z][_a-zA-Z0-9]*")) goto L56
    a2.clear()
    instrumentation_static_stackpointer = (instrumentation_static_stackpointer - 1 + 16) % 16
    return 0
    
    L56:
    if (s.toCharArray()[0] != 95) goto L87
    a2.clear()
    instrumentation_static_stackpointer = (instrumentation_static_stackpointer - 1 + 16) % 16
    return 0
    
    L87:
    instrumentation_static_stackpointer = (instrumentation_static_stackpointer - 1 + 16) % 16
    return 1
    

    问题在于,在字节码版本 51.0+ 中,大概是您正在使用的版本,必须在 StackMapTable 属性中包含有关字节码变量的类型信息。但是,您没有更新它以包含新变量 a2 的类型。堆栈映射表仅在有跳转或跳转目标时才被查阅。因此,当它到达 L56 时,它会查阅表格以查看传入的类型是什么,并且表格显示唯一的变量是 s,类型为 String。然后它到达a2.clear(),发现您正在使用堆栈映射表中不存在的变量,并且验证失败。

    【讨论】:

    • 好的,谢谢!如何将变量添加到StackMapTable?我希望visitLocalVariable 能为我做这件事……
    • 你肯定想使用标志COMPUTE_FRAMESClassWriter 为你做这件事……
    猜你喜欢
    • 1970-01-01
    • 2013-04-06
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 2013-03-12
    • 1970-01-01
    相关资源
    最近更新 更多