Guardsquare / proguard

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

"Stack size becomes negative" Exception when using certain framework #202

Open Hydings opened 2 years ago

Hydings commented 2 years ago

Project Setup

In the reproducer we have 2 projects. One is using java 7 has a dependencie to StreamSupport and the java 8 one which also uses a framework that need java 8 (here its eclipse collections, but thats not the only framework creating the problem).

The Error

When running the proguard task on the java 7 project it gives an error Stack size becomes negative after instruction [5] invokevirtual #26 = Methodref(java/util/DoubleSummaryStatistics.accept(D)V) in [org/eclipse/collections/api/FloatIterable$$Lambda$13.value(F)V] . For me it looks like it tries to replace a something with StreamSupport that it shouldnt.

Reproducer

proguardTest2.zip

shannah commented 2 years ago

Update

After investigation it appears that the problem was in my java runtime jar that I was running proguard against. It had defined Charset.forName(String) as non-static, where it should have been static. Fixing that issue, fixed this crash.

Leaving the rest of the report here in case it helps other users in the future, but this is not an issue with proguard:

Original Issue

I'm also getting this issue, but when processing the kotlin-stdlib-1.4.32.jar.

Specifically on the <clinit> of kotlin.text.Charsets

If I use the -dontoptimize flag, this error does not occur.

Stacktrace: (for 7.1.1)

Initializing...
[INFO] Ignoring unused library classes...
[INFO]   Original number of library classes: 1914
[INFO]   Final number of library classes:    369
[INFO] Marking classes and class members to be kept...
[INFO] Inlining subroutines...
[INFO] Shrinking...
[INFO] Removing unused program classes and class elements...
[INFO]   Original number of program classes:            3470
[INFO]   Final number of program classes:               1524
[INFO] Optimizing (pass 1/1)...
[INFO] Unexpected error while computing stack sizes:
[INFO]   Class       = [kotlin/text/Charsets]
[INFO]   Method      = [<clinit>()V]
[INFO]   Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])
[INFO] Unexpected error while editing code:
[INFO]   Class       = [kotlin/text/Charsets]
[INFO]   Method      = [<clinit>()V]
[INFO]   Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])
[INFO] java.lang.IllegalArgumentException: Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V]
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.evaluateInstructionBlock(StackSizeComputer.java:345)
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.visitCodeAttribute0(StackSizeComputer.java:159)
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.visitCodeAttribute(StackSizeComputer.java:121)
[INFO]  at proguard.classfile.editor.StackSizeUpdater.visitCodeAttribute(StackSizeUpdater.java:44)
[INFO]  at proguard.classfile.editor.CodeAttributeEditor.visitCodeAttribute0(CodeAttributeEditor.java:592)
[INFO]  at proguard.classfile.editor.CodeAttributeEditor.visitCodeAttribute(CodeAttributeEditor.java:527)
[INFO]  at proguard.classfile.editor.MethodInvocationFixer.visitCodeAttribute(MethodInvocationFixer.java:66)
[INFO]  at proguard.classfile.attribute.CodeAttribute.accept(CodeAttribute.java:138)
[INFO]  at proguard.classfile.ProgramMethod.attributesAccept(ProgramMethod.java:148)
[INFO]  at proguard.classfile.attribute.visitor.AllAttributeVisitor.visitProgramMember(AllAttributeVisitor.java:94)
[INFO]  at proguard.classfile.visitor.MemberVisitor.visitProgramMethod(MemberVisitor.java:57)
[INFO]  at proguard.classfile.ProgramMethod.accept(ProgramMethod.java:140)
[INFO]  at proguard.classfile.ProgramClass.methodsAccept(ProgramClass.java:695)
[INFO]  at proguard.classfile.visitor.AllMemberVisitor.visitAnyClass(AllMemberVisitor.java:47)
[INFO]  at proguard.classfile.visitor.ClassVisitor.visitProgramClass(ClassVisitor.java:40)
[INFO]  at proguard.classfile.ProgramClass.accept(ProgramClass.java:544)
[INFO]  at proguard.classfile.ClassPool.classesAccept(ClassPool.java:285)
[INFO]  at proguard.optimize.Optimizer.execute(Optimizer.java:1322)
[INFO]  at proguard.ProGuard.optimize(ProGuard.java:548)
[INFO]  at proguard.ProGuard.execute(ProGuard.java:191)
[INFO]  at proguard.ProGuard.main(ProGuard.java:717)

Stacktrace for 7.2.0-beta2


[INFO] Initializing...
[INFO] Ignoring unused library classes...
[INFO]   Original number of library classes: 1914
[INFO]   Final number of library classes:    369
[INFO] Marking classes and class members to be kept...
[INFO] Inlining subroutines...
[INFO] Shrinking...
[INFO] Removing unused program classes and class elements...
[INFO]   Original number of program classes:            3470
[INFO]   Final number of program classes:               1524
[INFO] Optimizing (pass 1/1)...
[INFO] java.lang.IllegalArgumentException: Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V]
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.evaluateInstructionBlock(StackSizeComputer.java:348)
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.visitCodeAttribute0(StackSizeComputer.java:162)
[INFO]  at proguard.classfile.attribute.visitor.StackSizeComputer.visitCodeAttribute(StackSizeComputer.java:124)
[INFO]  at proguard.classfile.editor.StackSizeUpdater.visitCodeAttribute(StackSizeUpdater.java:44)
[INFO]  at proguard.classfile.editor.CodeAttributeEditor.visitCodeAttribute0(CodeAttributeEditor.java:595)
[INFO]  at proguard.classfile.editor.CodeAttributeEditor.visitCodeAttribute(CodeAttributeEditor.java:530)
[INFO]  at proguard.classfile.editor.MethodInvocationFixer.visitCodeAttribute(MethodInvocationFixer.java:66)
[INFO]  at proguard.classfile.attribute.CodeAttribute.accept(CodeAttribute.java:138)
[INFO]  at proguard.classfile.ProgramMethod.attributesAccept(ProgramMethod.java:148)
[INFO]  at proguard.classfile.attribute.visitor.AllAttributeVisitor.visitProgramMember(AllAttributeVisitor.java:94)
[INFO]  at proguard.classfile.visitor.MemberVisitor.visitProgramMethod(MemberVisitor.java:57)
[INFO]  at proguard.classfile.ProgramMethod.accept(ProgramMethod.java:140)
[INFO]  at proguard.classfile.ProgramClass.methodsAccept(ProgramClass.java:695)
[INFO]  at proguard.classfile.visitor.AllMemberVisitor.visitAnyClass(AllMemberVisitor.java:47)
[INFO]  at proguard.classfile.visitor.ClassVisitor.visitProgramClass(ClassVisitor.java:40)
[INFO]  at proguard.classfile.ProgramClass.accept(ProgramClass.java:544)
[INFO]  at proguard.classfile.ClassPool.classesAccept(ClassPool.java:285)
[INFO]  at proguard.optimize.Optimizer.execute(Optimizer.java:1322)
[INFO]  at proguard.ProGuard.optimize(ProGuard.java:548)
[INFO]  at proguard.ProGuard.execute(ProGuard.java:191)
[INFO]  at proguard.ProGuard.main(ProGuard.java:717)
[INFO] Unexpected error while computing stack sizes:  Class       = [kotlin/text/Charsets]  Method      = [<clinit>()V]  Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])Unexpected error while editing code:  Class       = [kotlin/text/Charsets]  Method      = [<clinit>()V]  Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])

Stacktrace for 6.3.0-beta2

Initializing...
[INFO] Ignoring unused library classes...
[INFO]   Original number of library classes: 1914
[INFO]   Final number of library classes:    369
[INFO] Marking classes and class members to be kept...
[INFO] Inlining subroutines...
[INFO] Shrinking...
[INFO] Removing unused program classes and class elements...
[INFO]   Original number of program classes:            3470
[INFO]   Final number of program classes:               1524
[INFO] Optimizing (pass 1/1)...
[INFO] java.lang.RuntimeException: java.lang.ArrayIndexOutOfBoundsException: -1
[INFO]  at proguard.classfile.visitor.ParallelAllClassVisitor.visitClassPool(ParallelAllClassVisitor.java:126)
[INFO]  at proguard.classfile.ClassPool.accept(ClassPool.java:111)
[INFO]  at proguard.optimize.Optimizer$TimedClassPoolVisitor.visitClassPool(Optimizer.java:1731)
[INFO]  at proguard.optimize.info.RepeatedClassPoolVisitor.visitClassPool(RepeatedClassPoolVisitor.java:78)
[INFO]  at proguard.classfile.ClassPool.accept(ClassPool.java:111)
[INFO]  at proguard.optimize.Optimizer.execute(Optimizer.java:545)
[INFO]  at proguard.ProGuard.optimize(ProGuard.java:504)
[INFO]  at proguard.ProGuard.execute(ProGuard.java:169)
[INFO]  at proguard.ProGuard.main(ProGuard.java:651)
[INFO] Caused by: java.lang.ArrayIndexOutOfBoundsException: -1
[INFO]  at proguard.evaluation.Stack.getTop(Stack.java:216)
[INFO]  at proguard.optimize.info.ParameterEscapeMarker.markEscapingParameters(ParameterEscapeMarker.java:463)
[INFO]  at proguard.optimize.info.ParameterEscapeMarker.visitParameter(ParameterEscapeMarker.java:433)
[INFO]  at proguard.classfile.util.AllParameterVisitor.visitParameters(AllParameterVisitor.java:178)
[INFO]  at proguard.classfile.util.AllParameterVisitor.visitLibraryMethod(AllParameterVisitor.java:80)
[INFO]  at proguard.classfile.LibraryMethod.accept(LibraryMethod.java:66)
[INFO]  at proguard.classfile.LibraryMember.accept(LibraryMember.java:86)
[INFO]  at proguard.classfile.constant.RefConstant.referencedMemberAccept(RefConstant.java:126)
[INFO]  at proguard.optimize.info.ParameterEscapeMarker.visitAnyMethodrefConstant(ParameterEscapeMarker.java:414)
[INFO]  at proguard.classfile.util.SimplifiedVisitor.visitMethodrefConstant(SimplifiedVisitor.java:229)
[INFO]  at proguard.classfile.constant.MethodrefConstant.accept(MethodrefConstant.java:69)
[INFO]  at proguard.classfile.ProgramClass.constantPoolEntryAccept(ProgramClass.java:600)
[INFO]  at proguard.optimize.info.ParameterEscapeMarker.visitConstantInstruction(ParameterEscapeMarker.java:315)
[INFO]  at proguard.classfile.instruction.visitor.MultiInstructionVisitor.visitConstantInstruction(MultiInstructionVisitor.java:85)
[INFO]  at proguard.optimize.evaluation.InstructionUsageMarker$MyNecessaryInstructionFilter.visitConstantInstruction(InstructionUsageMarker.java:1747)
[INFO]  at proguard.classfile.instruction.ConstantInstruction.accept(ConstantInstruction.java:157)
[INFO]  at proguard.classfile.attribute.CodeAttribute.instructionsAccept(CodeAttribute.java:178)
[INFO]  at proguard.classfile.attribute.CodeAttribute.instructionsAccept(CodeAttribute.java:150)
[INFO]  at proguard.classfile.instruction.visitor.AllInstructionVisitor.visitCodeAttribute(AllInstructionVisitor.java:54)
[INFO]  at proguard.classfile.attribute.visitor.MultiAttributeVisitor.visitCodeAttribute(MultiAttributeVisitor.java:285)
[INFO]  at proguard.classfile.attribute.visitor.DebugAttributeVisitor.visitCodeAttribute(DebugAttributeVisitor.java:331)
[INFO]  at proguard.classfile.attribute.CodeAttribute.accept(CodeAttribute.java:141)
[INFO]  at proguard.classfile.ProgramMethod.attributesAccept(ProgramMethod.java:134)
[INFO]  at proguard.classfile.attribute.visitor.AllAttributeVisitor.visitProgramMember(AllAttributeVisitor.java:95)
[INFO]  at proguard.classfile.util.SimplifiedVisitor.visitProgramMethod(SimplifiedVisitor.java:94)
[INFO]  at proguard.optimize.OptimizationInfoMemberFilter.visitProgramMethod(OptimizationInfoMemberFilter.java:99)
[INFO]  at proguard.classfile.ProgramMethod.accept(ProgramMethod.java:126)
[INFO]  at proguard.classfile.ProgramClass.methodsAccept(ProgramClass.java:651)
[INFO]  at proguard.classfile.visitor.AllMethodVisitor.visitProgramClass(AllMethodVisitor.java:47)
[INFO]  at proguard.classfile.ProgramClass.accept(ProgramClass.java:493)
[INFO]  at proguard.classfile.visitor.ParallelAllClassVisitor$MyThreadedClassVisitor$1.run(ParallelAllClassVisitor.java:197)
[INFO]  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
[INFO]  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[INFO]  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[INFO]  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[INFO]  at java.lang.Thread.run(Thread.java:748)
shannah commented 2 years ago

Update

After investigation it appears that the problem was in my java runtime jar that I was running proguard against. It had defined Charset.forName(String) as non-static, where it should have been static. Fixing that issue, fixed this crash.

Leaving the rest of the report here in case it helps other users in the future, but this is not an issue with proguard:

Original Issue

Some additional information, as I'm debugging this now.

The problem seems to be in a rewriting of the bytecodes for this method before the optimization step because the bytecodes reported in the StackSizeComputer debug output don't match the bytecodes reported for the original class by javap.

The original bytecodes of the <clinit> method, as reported by javap:


static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=1, args_size=0
         0: new           #2                  // class kotlin/text/Charsets
         3: dup
         4: invokespecial #83                 // Method "<init>":()V
         7: astore_0
         8: aload_0
         9: putstatic     #85                 // Field INSTANCE:Lkotlin/text/Charsets;
        12: ldc           #87                 // String UTF-8
        14: invokestatic  #28                 // Method java/nio/charset/Charset.forName:(Ljava/lang/String;)Ljava/nio/charset/Charset;
        17: dup

... snippet removed...

Here is an excerpt from the the stacktrace including debug output from StackSizeComputer when performing this.

 StackSizeComputer: kotlin/text/Charsets.<clinit>()V
[INFO] -- instruction block:
[INFO] [0]: 0 - 0 + 1 = 1: [0] new #2 = Class(kotlin/text/Charsets)
[INFO] [3]: 1 - 1 + 0 = 0: [3] invokespecial #38 = Methodref(kotlin/text/Charsets.<init>()V)
[INFO] [6]: 0 - 0 + 1 = 1: [6] ldc #42 = String("UTF-8")
[INFO] [8]: 1 - 2 + 1 = 0: [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO] Unexpected error while computing stack sizes:  Class       = [kotlin/text/Charsets]  Method      = [<clinit>()V]  Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])+ Method:       <clinit>()V
[INFO]   Access flags: 0x8
[INFO]     = static void <clinit>()
[INFO]   Class member attributes (count = 1):
[INFO]   - Code attribute instructions (code length = 71, locals = 0, stack = 3):
[INFO]     [0] new #2 = Class(kotlin/text/Charsets)
[INFO]     [3] invokespecial #38 = Methodref(kotlin/text/Charsets.<init>()V)
[INFO]     [6] ldc #42 = String("UTF-8")
[INFO]     [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [11] dup
[INFO]     [12] ldc #44 = String("Charset.forName("UTF-8")")
[INFO]     [14] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [17] putstatic #46 = Fieldref(kotlin/text/Charsets.UTF_8 Ljava/nio/charset/Charset;)
[INFO]     [20] ldc #48 = String("UTF-16")
[INFO]     [22] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [25] ldc #50 = String("Charset.forName("UTF-16")")
[INFO]     [27] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [30] ldc #54 = String("UTF-16BE")
[INFO]     [32] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [35] ldc #56 = String("Charset.forName("UTF-16BE")")
[INFO]     [37] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [40] ldc #60 = String("UTF-16LE")
[INFO]     [42] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [45] ldc #62 = String("Charset.forName("UTF-16LE")")
[INFO]     [47] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [50] ldc #66 = String("US-ASCII")
[INFO]     [52] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [55] ldc #68 = String("Charset.forName("US-ASCII")")
[INFO]     [57] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [60] ldc #72 = String("ISO-8859-1")
[INFO]     [62] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)
[INFO]     [65] ldc #74 = String("Charset.forName("ISO-8859-1")")
[INFO]     [67] invokestatic #23 = Methodref(kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue(Ljava/lang/Object;Ljava/lang/String;)V)
[INFO]     [70] return
[INFO]     Code attribute exceptions (count = 0):
[INFO]     Code attribute attributes (attribute count = 1):
[INFO]     - Line number table attribute (count = 7):
[INFO]       [0] -> line 24
[INFO]       [6] -> line 29
[INFO]       [20] -> line 36
[INFO]       [30] -> line 42
[INFO]       [40] -> line 48
[INFO]       [50] -> line 55
[INFO]       [60] -> line 61
[INFO] Unexpected error while editing code:  Class       = [kotlin/text/Charsets]  Method      = [<clinit>()V]  Exception   = [java.lang.IllegalArgumentException] (Stack size becomes negative after instruction [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;) in [kotlin/text/Charsets.<clinit>()V])

The problem appears to be the [8] invokevirtual instruction:

 [8] invokevirtual #17 = Methodref(java/nio/charset/Charset.forName(Ljava/lang/String;)Ljava/nio/charset/Charset;)

java.nio.charset.CharSet.forName() is a static method, but there is an invokevirtual instruction here. This results in the negative stack size because invokevirtual expects an extra reference to be on the stack.

Not sure where this is being misapplied yet.

mrjameshamilton commented 2 years ago

Hi @shannah , great that you've solved your issue.

Hi @Hydings, do you still have your problem? I was not about to reproduce the problem. It builds without the error and the output seems correct:

Test-Application. Works if it says java8.util...
interface java8.util.stream.Stream
Hydings commented 2 years ago

Hey @mrjameshamilton, sadly i still have the same error running the sample. I tried it on two different computers now. Both running Java11. Do you have any idea, what might be wrong? What JDK are you using? I used 11 coretto.

axitasavani commented 4 months ago

Hey! I have foud the same issue any update on this?

mrjameshamilton commented 4 months ago

Hi @axitasavani! There can be many different reasons for a "Stack size becomes negative" exception. Can you provide a sample that reproduces your issue?