Menu

#735 Proguard corrupts RuntimeInvisibleParameterAnnotations/RuntimeVisibleParameterAnnotations

v6.0
closed-fixed
None
Medium
2019-05-14
2018-11-22
No

On attempt to obfuscate bytecode for next Java enum class I got corrupted RuntimeInvisibleParameterAnnotations/RuntimeVisibleParameterAnnotations fields

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public enum  AnnotationInEnumConstructor {
    OK("123");

    @Retention(RetentionPolicy.CLASS)
    public @interface Foo {}

    AnnotationInEnumConstructor(@Foo String foo) {}

    public static void main(String[] args) {

    }
}

Bytecode produced by java compiler:

  private AnnotationInEnumConstructor(java.lang.String);
    descriptor: (Ljava/lang/String;ILjava/lang/String;)V
    flags: (0x0002) ACC_PRIVATE
       ....
    Signature: #33                          // (Ljava/lang/String;)V
    RuntimeInvisibleParameterAnnotations:
      parameter 0:
        0: #35()
          AnnotationInEnumConstructor$Foo

After obfuscation:

  private AnnotationInEnumConstructor(java.lang.String);
    descriptor: (Ljava/lang/String;ILjava/lang/String;)V
     ....
    RuntimeInvisibleParameterAnnotations:
      parameter 0:
      parameter 1:
      parameter 2:
        0: #38()
          AnnotationInEnumConstructor$Foo
    Signature: #25                          // (Ljava/lang/String;)V
2 Attachments

Discussion

  • max-kammerer

    max-kammerer - 2018-11-22

    With 'Runtime' retention for annotations, next code works differently before and after obfuscations of AnnotationInEnumConstructor.class:

    public class Foo {
    
        public static void main(String[] args) {
            System.out.println(
                    AnnotationInEnumConstructor.class.getDeclaredConstructors()[0].getParameterAnnotations()[0][0].annotationType().getName()
            );
            //before proguarding : AnnotationInEnumConstructor$Foo
    
            //after proguarding :
            // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
            // at Foo.main(Foo.java:5)
        }
    }
    
     
  • max-kammerer

    max-kammerer - 2018-11-26

    Same problem with inner classes:

    public class Outer {
        @Retention(RetentionPolicy.RUNTIME)
        public @interface Foo {}
    
        class Inner {
            public String foo;
    
            public Inner(@Foo String foo) {
                this.foo = foo;
            }
        }
    }
    

    Before transformation:

        RuntimeVisibleParameterAnnotations:
          parameter 0:
            0: #22()
    

    After transformation

    RuntimeVisibleParameterAnnotations:
          parameter 0:
          parameter 1:
            0: #19()
    
     
  • Eric Lafortune

    Eric Lafortune - 2018-11-27

    Thanks for your detailed report. I can reproduce the output. It appears that javap helpfully prints out its own interpretation of the RuntimeInvisibleParameterAnnotations attribute. Based on the preceding Signature attribute it prints out that Foo is an annotation for parameter #0, although the bytecode does specify that it is an annotation for parameter #2. With ProGuard's raw bytecode dump (-dontshrink -dontoptimize -dontobfuscate -dontpreverify -dump), you can see essentially the same bytecode before and after processing:

    Attributes in original code

        - Signature attribute:
          - Utf8 [(Ljava/lang/String;)V]
        - Runtime invisible parameter annotations attribute (parameter count = 3):
          - Parameter #2, annotation [LAnnotationInEnumConstructor$Foo;]:
    

    Attributes in processed code

        - Runtime invisible parameter annotations attribute (parameter count = 3):
          - Parameter #2, annotation [LAnnotationInEnumConstructor$Foo;]:
        - Signature attribute:
          - Utf8 [(Ljava/lang/String;)V]
    

    I assume the difference with javap is caused by the order of the attributes, which should never matter. The question is now: does it matter to the JVM and do you get any issues at runtime?

     
  • Eric Lafortune

    Eric Lafortune - 2018-11-27
    • status: open --> open-works-for-me
    • assigned_to: Eric Lafortune
     
  • Eric Lafortune

    Eric Lafortune - 2018-11-27

    Oops, I see your example now. We'll investigate.

     
  • Eric Lafortune

    Eric Lafortune - 2018-11-27
    • status: open-works-for-me --> open-accepted
     
  • Eric Lafortune

    Eric Lafortune - 2018-12-08
     
  • Eric Lafortune

    Eric Lafortune - 2018-12-08

    The problem is caused by JVM correlating the annotations with the method's signature attribute, instead of the method descriptor. This is a long-standing unresolved issue in the JDK, eg. JDK-8024694 and JDK-8062582. We'll see if we can avoid it.

     
  • Eric Lafortune

    Eric Lafortune - 2018-12-29
    • status: open-accepted --> open-fixed
     
  • Eric Lafortune

    Eric Lafortune - 2018-12-29

    The problem has now been fixed for the upcoming ProGuard 6.1.0 beta2.

     
  • Emin Kokalari

    Emin Kokalari - 2018-12-30

    ProGuard 6.1.0 beta2 cannot be resolved, when it will be ready to download ? Thanks for your great work

     
  • Eric Lafortune

    Eric Lafortune - 2019-05-14
    • Status: open-fixed --> closed-fixed
     

Log in to post a comment.