javajvmjvm-bytecode

Is it possible for a Java classfile StackMap to model a "broken" this instance?


In this answer it is stated:

It is impossible to create an exception handler covering the entire constructor, including the super(…), with stack maps.

I mostly believe the above statement but still have a lingering doubt, because later (in a comment) it's stated:

Generally, frames do not need to describe what has been there in the previous code, but what will be used in the subsequent code.

This makes it sound like it might be possible for the StackMap to simply omit the "this" instance, and then as long as the exception handler code doesn't touch it, it would still verify.

To be specific, I know you can't compile this code, but suppose you could:

public class TryCtor {
    public TryCtor() {
        try {
            super();
        } catch (Error e) {
            throw new RuntimeException();
        }
    }
}

Note that this is not referenced at all in the exception handler.

You would then get a classfile that contained bytecode looking like this:

public class TryCtor {
  TryCtor();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: goto          16
       7: astore_1
       8: new           #9                  // class java/lang/RuntimeException
      11: dup
      12: invokespecial #11                 // Method java/lang/RuntimeException."<init>":()V
      15: athrow
      16: return
    Exception table:
       from    to  target type
           0     4     7   Class java/lang/Error
}

So my question is: Is it correct to say that there's no possible StackMap that could be included in such a classfile that would allow it to verify?

Even if you specified top, or an empty stack, or whatever?

For the purposes of this question, assume a modern JVM version using StackMaps.

Thanks.


Solution

  • Short answer: No. The problem is that the state of the new object is non-deterministic when the exception handler is run. Did the exception occur before bci 1, in which case the object is still uninitialized, or did the exception occur at bci 4, in which case the object is initialized?

    In general, it's unlikely for an exception to be thrown by a load or store instruction, but it can happen when if Thread.stop method is called at the right moment. Note that the stop method is being removed in Java 20, but debuggers might still be able to stop threads in a similar fashion.

    I was curious to see what would happen if the stack map was hacked such that the object type was top, but it failed to verify. The problem is that top doesn't match uninitialized this or a fully initialized instance.