【问题标题】:Java Access Flag VerificationJava 访问标志验证
【发布时间】:2015-04-08 20:17:24
【问题描述】:

考虑以下场景:

package packA;

public class A
{
    private static int i = 0;
}

package packB;

public class B
{
    public static void main(String[] args)
    {
        int i = A.i + 1;
    }
}

因为A.i 是private,所以无法从类B 访问它,这样做会导致编译器错误。

但是,如果我像这样检测 B 类

public class B
{
    public static void main(String[] args)
    {
        // getstatic packA/A.i : I
        // iconst_1
        // iadd
        // istore_1
    }
}

调用main方法,JVM验证器会检查访问A.i是否有效?同样,如果A.i 被声明为final,它是否会将以下字节码视为有效?

public class B
{
    public static void main(String[] args)
    {
        // iconst_2
        // putstatic packA/A.i : I
    }
}

我对此感到疑惑,因为验证程序需要加载类来检查字段的访问标志。此外,如果它不验证它们,则可能会检测恶意字节码来更改字段的值或提供允许访问字段而无需反射的“黑客方法”。

【问题讨论】:

  • 到目前为止,JVMS 已经完全涵盖了您提出的许多问题。最后不是学习规范的好时机吗? 5.4.3. Resolution 清楚地回答了您的问题:在字段解析期间检查了访问标志;是的,如果需要,在此过程中会加载引用的类。
  • 我在验证评论中的一些评论后更新了我的答案。事实证明,我的原始答案和评论都不正确。

标签: java jvm javac bytecode-manipulation


【解决方案1】:

访问对访问类不可见的字段会导致验证错误。为了应用此验证,JVM 需要加载和链接声明此字段的类。这是正常的验证过程,例如,JVM 还需要加载类以检查方法是否存在。然而,验证和所需的类加载可以延迟应用,即在第一次执行方法之前。

HotSpot 上有一个例外,其中一个类扩展了作为内部接口的 MagicAccessorImpl。验证者跳过此类类的访问级别验证。这是 JVM 的内部代码生成所必需的,其中反射代码被优化为字节码。另一个例外是通过匿名类加载器加载的类,它继承另一个类可见性上下文,例如 lambda 类。

对于内部类,javac 将包私有访问器方法插入到声明类中,以便内部类可以访问其外部类的字段。

编辑:我在发表评论后编辑了我的答案,但现在有时间验证这一点,最终字段的处理确实与我的第一个答案略有不同,但也与评论所说的相比:

  1. 对于构造函数,验证器仅验证最​​终字段分配是从构造函数内部执行的,即字节码级别上名为 <init> 的方法。因此,在字节码中可以出现以下情况,而在 Java 代码中是不允许的:

    class Foo {
      final int bar; // is 0
      Foo() { }
    }
    
    class Foo {
      final int bar; // is 2
      Foo() { 
        bar = 1;
        bar = 2;
      }
    }
    
    class Foo {
      final int bar; // is 2
      Foo() { 
        this(null);
        bar = 2;
      }
      Foo(Void v) {
        bar = 1;
      }
    }
    
  2. 对于静态字段,Java 编译器没有这样的限制,即以下 Java 代码在字节码中是合法的:

    class Foo {
      static final int bar; // is 0
      static { }
    }
    
    
    class Foo {
      static final int bar; // is 2
      static {
        bar = 1;
        bar = 2;
      }
    }
    
    class Foo {
      static final int bar; // is 2
      static {
        bar = 1;
        foobar(2);
      }
      static foobar(int i) {
        bar = i;
      }
    }
    
    Foo.foobar(3); // bar is 3
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 2013-11-26
    • 1970-01-01
    相关资源
    最近更新 更多