Skip to content

Instantly share code, notes, and snippets.

@suztomo
Last active September 16, 2020 15:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save suztomo/c83b2cbde059242e907c907e64e003e2 to your computer and use it in GitHub Desktop.
Save suztomo/c83b2cbde059242e907c907e64e003e2 to your computer and use it in GitHub Desktop.
Class reference causing a runtime error

Class Literal Causing a IllegalAccessError

Class C

package bar;

import foo.D;

public class C {
  public static void main(String[] arguments) {
    Class k = D.class;
    System.out.println(k);
  }
}

Class D is public when Class C is compiled.

package foo;

public class D {

}

Executing bar.C shows "class foo.D".

suztomo-macbookpro44% mvn clean package 
...
suztomo-macbookpro44% mv target/linkage-checker-test-1.0-SNAPSHOT.jar d-public.jar
suztomo-macbookpro44% java -cp target/classes:d-public.jar bar.C                  
class foo.D

We change class D to be non-public:

package foo;

class D {

}

(corresponding lines in class C are commented out)

suztomo-macbookpro44% mvn clean compile 
...
# We don't need this version of class bar.C.
suztomo-macbookpro44% rm -rf target/classes/bar 

Then run the class C and class. I get IllegalAccessError just to touch the class literal.

suztomo-macbookpro44% java -cp target/classes:d-public.jar bar.C     
Exception in thread "main" java.lang.IllegalAccessError: tried to access class foo.D from class bar.C
	at bar.C.main(C.java:27)

How does that happen?

suztomo-macbookpro44% javap -private -verbose  -cp d-public.jar bar.C
Classfile jar:file:/Users/suztomo/wsgenoptions/d-public.jar!/bar/C.class
  Last modified Sep 15, 2020; size 553 bytes
  MD5 checksum c7adc0c202c8e66d94db0748bdc7f551
  Compiled from "C.java"
public class bar.C
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#22         // java/lang/Object."<init>":()V
   #2 = Class              #23            // foo/D
   #3 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #26.#27        // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #5 = Class              #28            // bar/C
   #6 = Class              #29            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lbar/C;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               arguments
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               k
  #19 = Utf8               Ljava/lang/Class;
  #20 = Utf8               SourceFile
  #21 = Utf8               C.java
  #22 = NameAndType        #7:#8          // "<init>":()V
  #23 = Utf8               foo/D
  #24 = Class              #30            // java/lang/System
  #25 = NameAndType        #31:#32        // out:Ljava/io/PrintStream;
  #26 = Class              #33            // java/io/PrintStream
  #27 = NameAndType        #34:#35        // println:(Ljava/lang/Object;)V
  #28 = Utf8               bar/C
  #29 = Utf8               java/lang/Object
  #30 = Utf8               java/lang/System
  #31 = Utf8               out
  #32 = Utf8               Ljava/io/PrintStream;
  #33 = Utf8               java/io/PrintStream
  #34 = Utf8               println
  #35 = Utf8               (Ljava/lang/Object;)V
{
  public bar.C();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 25: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lbar/C;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #2                  // class foo/D
         2: astore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        10: return
      LineNumberTable:
        line 27: 0
        line 28: 3
        line 29: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0 arguments   [Ljava/lang/String;
            3       8     1     k   Ljava/lang/Class;
}
SourceFile: "C.java"

The bytecode above has 0: ldc #2 // class foo/D to load the class D, which we change non-public at runtime. This causes the IllegalAccessError as documented in https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.ldc.

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