Should set flagThisUninit whenever uninitializedThis is on the stack

XMLWordPrintable

    • Type: CSR
    • Resolution: Unresolved
    • Priority: P3
    • 27
    • Component/s: hotspot
    • None
    • binary
    • minimal
    • adjusts verification of certain unreachable code in generated class files
    • Class file construct
    • SE

      Summary

      If a class file contains a StackMapTable entry with an uninitializedThis type on the stack, the flagThisUninit verification flag should be set, preventing return until this is initialized.

      Problem

      In normal usage, an <init> method has, as its 0th local variable, an object of type uninitializedThis which has been newly-allocated and needs to be initialized. To properly initialize the object, the code is required by verification to invoke another <init> method (this() or super()), which replaces uninitializedThis with a normal LFoo; class type. Only then can the code return.

      The uninitializedThis object does not have to remain in the 0th local variable position. It may be copied elsewhere, overwritten, or pushed/popped on the stack. But verification keeps track of the initialization state of the current object via a flagThisUninit flag, which can only be "cleared" with a this()/super() call. The return instruction cannot be used until the flag has been cleared.

      The StackMapTable allows jump targets to declare their type state. This includes local variables or stack entries with type uninitializedThis. Whenever uninitializedThis appears as a local variable, the verifier implicitly sets flagThisUninit for that stack frame.

      As a corner case, an uninitializedThis type may appear in a StackMapTable entry as the type of a stack value, but not as the type of a local variable. In this case, current rules say that flagThisUninit should not be set. This has three implications:

      • The code is unreachable: method execution begins with flagThisUninit set, and the only way to unset it is to call super()/this(). But that call also removes any uninitializedThis types. So a candidate jump point will either have no uninitializedThis on its stack, or will have flagThisUninit set. Neither of those cases is compatible with the jump target's declared type state.

      • The intended initialization timing of the code is ambiguous: if this code is meant to execute before the super()/this() call, it should have flagThisUninit set; if this code is meant to execute after the super()/this() call, it should not be operating on type uninitializedThis.

      • The code can legally return, even though it has apparently not yet properly initialized this. (Fortunately, nothing bad can happen, because the code is unreachable. But we're one bug away from violating a core invariant of object initialization.)

      This StackMapTable corner case has no practical use, but introduces needless complexity when designing or trying to understand verification.

      Solution

      The solution is simple: for a StackMapTable entry that mentions uninitializedThis as one of its stack types, set the flagThisUninit flag.

      For existing class files, if they contain unreachable code as described above, and if that unreachable code attempts to return, a new verification error will occur. This can be addressed by, e.g., removing the unreachable code.

      Specification

      See JVMS issue JDK-8375481. The rule in 4.10.1.4 is changed as follows:

      If any ~~local variable in Locals has the type~~ type in Locals or OperandStack is uninitializedThis, then Flags has the single element flagThisUninit, otherwise Flags is an empty list.

            Assignee:
            Dan Smith
            Reporter:
            Dan Smith
            Chen Liang, Matias Saavedra Silva
            Votes:
            1 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: