Guardsquare / proguard

ProGuard, Java optimizer and obfuscator
https://www.guardsquare.com/en/products/proguard
GNU General Public License v2.0
2.82k stars 406 forks source link

Method of lambda expression passed as an instance of an interface defined in obfuscated library is not obfuscated correclty #405

Open patrick96 opened 4 months ago

patrick96 commented 4 months ago

When building against an obfuscated library (providing its mapping file using -applymapping), I have noticed that lambda expressions which represent an implementation of an interface do not get obfuscated correctly when that interface is part of the obfuscated library. At runtime this produces an AbstractMethodError because the interface's single member method was renamed, but the lambda instance still has the unobfuscated method name.

Steps to Reproduce

The idea is to build an obfuscated library defining an interface and an application defining a lambda

lib_stripped.jar

Interface.java

interface Interface {
  void doSomething(int x);
}

Lib.java

public class Lib {
  public static void call(Interface i, int x) {
    System.out.println("methods:");
    for (var m : i.getClass().getDeclaredMethods()) {
      System.out.println("\t" + m);
    }
    i.doSomething(x);
  }
}

lib.pro

-injars lib.jar
-outjars lib_stripped.jar

-libraryjars  <java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)

-printmapping lib_stripped.map

-useuniqueclassmembernames
-dontoptimize
-dontshrink

To build lib_stripped.jar:

javac Lib.java Interface.java
jar cf lib.jar Lib.class Interface.class
proguard-7.4.2/bin/proguard.sh -include lib.pro

app_stripped.jar

Main.java

public class Main {
  public static void main(String[] args) {
    Lib.call((x) -> { System.out.println(x + 1); }, 100);
  }
}

app.pro

-injars app.jar
-outjars app_stripped.jar

-libraryjars <java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)
-libraryjars lib.jar(!META-INF/versions/**,!module-info.class)

-applymapping lib_stripped.map
-printmapping app_stripped.map

-useuniqueclassmembernames
-dontoptimize
-dontshrink

-keep public class Main {
    public static void main(java.lang.String[]);
}

To build app_stripped.jar:

javac -cp lib.jar Main.java
jar -c -f app.jar -e Main.class Main.class
proguard-7.4.2/bin/proguard.sh -include app.pro

Now when we run this, we get an AbstractMethodError and see that the method defined on the passed object is still called doSomething:

$ java -cp lib_stripped.jar:app_stripped.jar Main
methods:
        public void Main$$Lambda/0x000071a1e8001800.doSomething(int)
Exception in thread "main" java.lang.AbstractMethodError: Receiver class Main$$Lambda/0x000071a1e8001800 does not define or inherit an implementation of the resolved method 'abstract void a(int)' of interface a.
        at b.a(Unknown Source)
        at Main.main(Unknown Source)

Additional context

If you run with the unobfuscated jars, you get the expected output:

$ java -cp lib.jar:app.jar Main
methods:
        public void Main$$Lambda/0x00007baadc001800.doSomething(int)
101