简体   繁体   中英

Java Access Flag Verification

Consider the following scenario:

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;
    }
}

Because Ai is private , it is not possible to access it from class B , and doing so would cause a compiler error.

However, if I instrument the B class like this

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

And call the main method, would the JVM verifier check if accessing Ai is valid? Similarly, would it pass the following bytecode as valid if Ai was declared final ?

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

I am wondering about this because the verifier would need to load the class to check the access flags of the fields. Also, if it doesn't verify them it would be possible to instrument malicious bytecode to change the values of fields or provide 'hack methods' that would allow accessing a field without reflection.

Accessing a field that is not visible to the accessing class causes a verification error. In order to apply this verification, the JVM needs to load and link the class that declares this field. This is normal verification procedure, the JVM also needs to load classes to check if methods exist, for example. Verifications and the required class loading can however be applied lazily, ie before the first execution of a method.

One exception is on HotSpot where a class extends MagicAccessorImpl which is an internal interface. The verifier skips the access level verification for such classes. This is required for the JVM's internal code generation where reflective code is optimized as byte code. Another exception would be clases that are loaded via an anonymous class loader which inherit another classes visibility context such as lambda classes.

For inner classes, javac inserts package-private accessor methods into the declaring class such that inner classes can access their outer class's field.

Edit: I edited my answer after a comment but now found the time to verify this and final fields are indeed treated slightly different to my first answer but also compared to what the comment said:

  1. For constructors, the verifier only verifies that a final field assignment is performed from within a constructor, ie a method named <init> on the byte code level. Therefore, the following is possible in byte code, what is not allowed in Java code:

     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. For the static fields, no such restriction is enforced for the Java compiler, ie the following Java code is legal in byte code:

     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 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM