JDK-8350029 : Illegal invokespecial interface not caught by verification
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 24
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2025-02-13
  • Updated: 2025-06-11
  • Resolved: 2025-06-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 25
25 b26Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
Verification of non-<init> invokespecial is expected to ensure that the referenced class is the current class, a superclass of the current class, or a direct superinterface of the current class (JVMS 4.9.2).

The specified mechanism for this check relied on subtyping, which is not quite right, and didn't account for interface methods. JVMS is being updated to directly test that the referenced class is one of the allowed classes (JDK-8122946).

HotSpot behavior since Java 8 seems to be to handle Methodrefs with a subtyping check, and InterfaceMethodrefs by matching a direct superinterface. This leaves a hole: a Methodref could name any interface, and the verifier would consider the current class to be a subtype of that interface. Verification would succeed, and an error would only occur at resolution.

The implementation of this check should be simplified to just test whether the class name given by the Methodref or InterfaceMethodref is:
1) the name of this class, the direct superclass, a direct superinterface; or
2) a name that, when loaded by the current loader*, produces one of the indirect superclasses

(*To avoid unnecessary loading, (2) should first test whether any indirect superclass has a matching name.)

Comments
Changeset: 8f8b367a Branch: master Author: David Holmes <dholmes@openjdk.org> Date: 2025-06-05 00:35:26 +0000 URL: https://git.openjdk.org/jdk/commit/8f8b367ae3c9afca3581f6aced7f9855ef0d541d
05-06-2025

Thanks [~heidinga]
04-06-2025

I attached the table showing the different cases between the old and new verifiers for {MethodRef, InterfaceMethodRef} x {Class, Direct super interface, other interface}. The spec update and PR change only 1 cell in the table - the MethodRef of an "other interface" goes from a Resolution error to a VerifyError
04-06-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/25538 Date: 2025-05-30 06:31:43 +0000
30-05-2025

The actual form of the solution is somewhat different to that outlined in the problem statement. We need to know if the target type for the invoke is an interface type. We can capture that information when the assignability test is done and the type is loaded.
30-05-2025

> When I correct the test to use an InterfaceMethodRef, I get the following VerifyError: > > java.lang.VerifyError: Bad invokespecial instruction: interface method reference is in an indirect superinterface. [~heidinga] note that although you got a VerifyError as desired, it is actually not the "right" VerifyError. In the test the interface is Runnable, which is neither a direct nor indirect interface of the test class - it is completely unrelated. You get the above error message because the code assumes that is_subtype actually reports correctly and so then checks if the method ref was actually an InterfaceMethodRef, in which case it assumes/expects the reference must be to a method in an indirect interface, when in actuality the method is in a completely unrelated interface. EDIT: to further clarify the code is simply reporting a misleading error message in this case.
14-05-2025

[~eosterlund] The idea of checking interfaces during verification is interesting and worthy of more discussion. Can we move that conversation to a separate RFE / issue? This one, and the related set of issues, are confusing enough without adding a new discussion track at this point.
06-05-2025

I for one would love for the JVM to be allowed to dismiss type unsafe garbage code at verification time instead of being forced to swallow it and throw at runtime. Why? Because it’s just too surprising that your List might actually be a String, and seems equally as wrong as letting an ArrayList be a String. Every time I see a compiler bug due to being unable to trust our own type system, I can’t help but think this is so unnecessary. We should be able to trust our own type systems and not get lied to. Harold helped me prototype such checks in the old interpreter and it was more complex due to the lack of stack maps. I guess that’s the main hassle; representing the union of interfaces that weird merged data flows can have without stack maps.
05-05-2025

Here's a test class (jasm format): --- public class C extends B version 52:0 { public static Method getI:()I stack 1 { iconst_3; ireturn; } public static Method test:"(LC;)V" stack 2 { aload_0; //invokespecial Method B.run:()V; // valid //invokespecial Method A.run:()V; // VerifyError invokespecial Method java/lang/Runnable.run:()V; // VerifyError? return; } } --- Given two other classes A and B, where C extends B but A is unrelated, the 'B.run()' invocation passes verification and the 'A.run()' invocation fails. The 'Runnable.run()' invocation ought to fail verification like 'A.run()'.
13-02-2025