Search code examples
javastaticjvmbytecode

Invokespecial with old semantics: call instance method of other class


According to this in the old java versions there was a less restrictive version of invokespecial called invokenonvirtual. This instruction would let you invoke instance methods without virtual lookup and ,if I got that right, those methods could belong to non-assignable classes to the current one. With invokespecial this changed as it is used to only invoke super, private or initializing methods.

My questions is: Is there a way with java 8 (or higher) to bypass those structural constraints [4.9.2] of the invokespecial instruction and call without virtual lookup a method of a different class (i.e a class that is not assignment compatible with the current class)

I could use the no-verify flag to disable the verification process, but is there a more elegant way?


Solution

  • You are conflating two different issues. invokenonvirtual and inokespecial are the same and always have been. They are just two different names for the same opcode.

    The question you linked is talking about a separate feature ACC_SUPER, which has a long and complicated history of its own.

    Basically, in very early versions of Java, compilation of super calls was broken. When compiling a superclass call, it would insert an invokespecial to the superclass method at time of compilation. If the class hierarchy was later changed, it would still attempt to call the method it was compiled to call, even if a new override was inserted at an intermediate point in the hierarchy.

    Note that this still didn't let you invoke methods in unrelated classes - the issue only related to different overrides of the same method being added in the same inheritance change after compilation.

    After the Java authors realized their mistake, they updated the JVM's handling of invokespecial to handle super calls correctly. Now, no matter which method is specified in the instruction, it will walk the superclass hierachy at link/runtime and call the appropriate method.

    However, they were worried that code compiled under old versions of Java was relying on the broken behavior, so for backwards compatibility, they added a new classfile flag, ACC_SUPER. If the flag is set on a class, it has the new (correct) behavior, if it doesn't, it uses the old behavior. Since old classfiles were compiled before the flag existed, they won't have it set. Meanwhile, the compiler was updated to set ACC_SUPER on all new classes and everyone was happy...

    Up until 2011 that is. It turns out that java.lang.Thread has a security sensitive method that users are not supposed to be able to call. To prevent people from calling it from subclasses of Thread, they overrode it to throw an exception. However, someone realized that hackers could define a subclass of Thread without the ACC_SUPER flag, and thus skip the security check and call the dangerous version of the method and break out of the Java sandbox.

    Unfortunately, the only way to fix this security vulnerability was to remove the ACC_SUPER feature entirely - that is to treat every class as if it has the flag set, whether it actually does or not. It was hurriedly fixed in Java 7 update 13, and in Java 8, the spec itself was changed to document that ACC_SUPER no longer had any effect.

    So the answer is no, there is no way to get the old behavior of super calls in any version of Java after 7u13, or whatever update it was that implemented the security fix.