JDK-8200167 : Validate more special case invocations
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 10,11
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2018-03-23
  • Updated: 2019-06-20
  • Resolved: 2018-05-01
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 11
11 b12Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
There is some laxity in the verifier when it comes to checking interface method calls, compared to non-interface calls. This has led to some missing receiver type checks in the VM invocation paths and MethodHandle invocation paths, which have been fixed over time. There remains an issue with invokespecial of interface methods.

JDK-8168699 dealt with direct bytecode invocation modes (ie invokespecial) but did not make any changes to Methodhandle invocations. JDK-8174962 made further changes for invokeInterface, including MethodHandle invocation, but private interface methods use invokeSpecial. So the case of a MethodHandle based invokeSpecial invocation is not covered.

If we take the test written for 8174962 and adapt it to use private methods then we hit an assertion failure during MethodHandle verification:

#  Internal Error (/export/users/dh198349/jdk-hs/open/src/hotspot/cpu/x86/macroAssembler_x86.cpp:895), pid=7516, tid=7545
#  assert(false) failed: DEBUG MESSAGE: receiver class disagrees with MemberName.clazz
#

The low-level check has detected the incorrect receiver type that should actually be detected at a higher level and IncompatibleClassChangeError thrown.
Comments
I've moved this from hotspot->runtime to corelibs->java.lang.invoke as 99% of the issue relates to the Java level MH code. The fix is ready to go out for review.
13-04-2018

Direct invocations hit the normal low-level checks for invokespecial. The fix for MH is to add a similar, but higher-level, check, that the receiver is a subtype of the caller. The VM compiler issue was caused by optimising away a checcast that was still needed at runtime.
13-04-2018

A fix for MH has been developed. But the test also shows missing checks in the compiler.
28-03-2018

The test has four cases that should throw ICCE due to the failed receiver type check. Two involve direct invocation. These tests "pass" but they hit a different ICCE: Thrown: java.lang.IllegalAccessError: Receiver class PrivateInterfaceCall$D1 must be the current class or a subtype of interface PrivateInterfaceCall$I2 Thrown: java.lang.IllegalAccessError: Receiver class PrivateInterfaceCall$E1 must be the current class or a subtype of interface PrivateInterfaceCall$I2 instead of Thrown: java.lang.IncompatibleClassChangeError: Class InterfaceCall$D1 does not implement the requested interface InterfaceC all$I2 So even though we don't perform the new receiver check, we're hitting the normal constraints on invokespecial // Check that the class of objectref (the receiver) is the current class or interface, // or a subtype of the current class or interface (the sender), otherwise invokespecial // throws IllegalAccessError. // The verifier checks that the sender is a subtype of the class in the I/MR operand. // The verifier also checks that the receiver is a subtype of the sender, if the sender is // a class. If the sender is an interface, the check has to be performed at runtime. InstanceKlass* sender = InstanceKlass::cast(current_klass); sender = sender->is_anonymous() ? sender->host_klass() : sender; if (sender->is_interface() && recv.not_null()) { Klass* receiver_klass = recv->klass(); if (!receiver_klass->is_subtype_of(sender)) { ResourceMark rm(THREAD); char buf[500]; jio_snprintf(buf, sizeof(buf), "Receiver class %s must be the current class or a subtype of interface %s", receiver_klass->name()->as_C_string(), sender->name()->as_C_string()); THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), buf); } } } so this seems to be okay. Though it should be examined whether we really expected to hit the new check in this case. For the MethodHandle based invocation we hit the assertion failure in the "stop" as part of MH verification. Specifying -XX:-VerifyMethodHandles the test no longer crashes but the MH invocation test successfully invokes the method instead of encountering the ICCE. The same is seen in product mode.
26-03-2018