8026065: InterfaceMethodref for invokespecial must name a direct superinterface
Summary: Add verification to check that invokespecial of an InterfaceMethodref names a method in a direct superinterface of the current class or interface in accordance with JSR 335, JVMS 4.9.2 Structural Constraints.
Reviewed-by: acorn, hseigel, coleenp
Contributed-by: lois.foltan@oracle.com
--- a/hotspot/src/share/vm/classfile/verifier.cpp Sat Nov 23 12:25:13 2013 +0100
+++ b/hotspot/src/share/vm/classfile/verifier.cpp Tue Nov 26 09:52:22 2013 -0500
@@ -2302,6 +2302,24 @@
}
}
+bool ClassVerifier::is_same_or_direct_interface(
+ instanceKlassHandle klass,
+ VerificationType klass_type,
+ VerificationType ref_class_type) {
+ if (ref_class_type.equals(klass_type)) return true;
+ Array<Klass*>* local_interfaces = klass->local_interfaces();
+ if (local_interfaces != NULL) {
+ for (int x = 0; x < local_interfaces->length(); x++) {
+ Klass* k = local_interfaces->at(x);
+ assert (k != NULL && k->is_interface(), "invalid interface");
+ if (ref_class_type.equals(VerificationType::reference_type(k->name()))) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void ClassVerifier::verify_invoke_instructions(
RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
bool *this_uninit, VerificationType return_type,
@@ -2432,23 +2450,38 @@
return;
}
} else if (opcode == Bytecodes::_invokespecial
- && !ref_class_type.equals(current_type())
+ && !is_same_or_direct_interface(current_class(), current_type(), ref_class_type)
&& !ref_class_type.equals(VerificationType::reference_type(
current_class()->super()->name()))) {
bool subtype = false;
+ bool have_imr_indirect = cp->tag_at(index).value() == JVM_CONSTANT_InterfaceMethodref;
if (!current_class()->is_anonymous()) {
subtype = ref_class_type.is_assignable_from(
current_type(), this, CHECK_VERIFY(this));
} else {
- subtype = ref_class_type.is_assignable_from(VerificationType::reference_type(
- current_class()->host_klass()->name()), this, CHECK_VERIFY(this));
+ VerificationType host_klass_type =
+ VerificationType::reference_type(current_class()->host_klass()->name());
+ subtype = ref_class_type.is_assignable_from(host_klass_type, this, CHECK_VERIFY(this));
+
+ // If invokespecial of IMR, need to recheck for same or
+ // direct interface relative to the host class
+ have_imr_indirect = (have_imr_indirect &&
+ !is_same_or_direct_interface(
+ InstanceKlass::cast(current_class()->host_klass()),
+ host_klass_type, ref_class_type));
}
if (!subtype) {
verify_error(ErrorContext::bad_code(bci),
"Bad invokespecial instruction: "
"current class isn't assignable to reference class.");
return;
+ } else if (have_imr_indirect) {
+ verify_error(ErrorContext::bad_code(bci),
+ "Bad invokespecial instruction: "
+ "interface method reference is in an indirect superinterface.");
+ return;
}
+
}
// Match method descriptor with operand stack
for (int i = nargs - 1; i >= 0; i--) { // Run backwards
--- a/hotspot/src/share/vm/classfile/verifier.hpp Sat Nov 23 12:25:13 2013 +0100
+++ b/hotspot/src/share/vm/classfile/verifier.hpp Tue Nov 26 09:52:22 2013 -0500
@@ -345,6 +345,9 @@
// that a class has been verified and prepared for execution.
bool was_recursively_verified() { return _klass->is_rewritten(); }
+ bool is_same_or_direct_interface(instanceKlassHandle klass,
+ VerificationType klass_type, VerificationType ref_class_type);
+
public:
enum {
BYTECODE_OFFSET = 1,
--- a/hotspot/src/share/vm/interpreter/linkResolver.cpp Sat Nov 23 12:25:13 2013 +0100
+++ b/hotspot/src/share/vm/interpreter/linkResolver.cpp Tue Nov 26 09:52:22 2013 -0500
@@ -915,6 +915,25 @@
return;
}
+ // check if invokespecial's interface method reference is in an indirect superinterface
+ if (!current_klass.is_null() && resolved_klass->is_interface()) {
+ Klass *klass_to_check = !InstanceKlass::cast(current_klass())->is_anonymous() ?
+ current_klass() :
+ InstanceKlass::cast(current_klass())->host_klass();
+
+ if (!InstanceKlass::cast(klass_to_check)->is_same_or_direct_interface(resolved_klass())) {
+ ResourceMark rm(THREAD);
+ char buf[200];
+ jio_snprintf(buf, sizeof(buf),
+ "Interface method reference: %s, is in an indirect superinterface of %s",
+ Method::name_and_sig_as_C_string(resolved_klass(),
+ resolved_method->name(),
+ resolved_method->signature()),
+ current_klass->external_name());
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf);
+ }
+ }
+
// check if not static
if (resolved_method->is_static()) {
ResourceMark rm(THREAD);
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Sat Nov 23 12:25:13 2013 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Nov 26 09:52:22 2013 -0500
@@ -1051,6 +1051,18 @@
return false;
}
+bool InstanceKlass::is_same_or_direct_interface(Klass *k) const {
+ // Verify direct super interface
+ if (this == k) return true;
+ assert(k->is_interface(), "should be an interface class");
+ for (int i = 0; i < local_interfaces()->length(); i++) {
+ if (local_interfaces()->at(i) == k) {
+ return true;
+ }
+ }
+ return false;
+}
+
objArrayOop InstanceKlass::allocate_objArray(int n, int length, TRAPS) {
if (length < 0) THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
if (length > arrayOopDesc::max_array_length(T_OBJECT)) {
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Sat Nov 23 12:25:13 2013 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Tue Nov 26 09:52:22 2013 -0500
@@ -777,6 +777,7 @@
// subclass/subinterface checks
bool implements_interface(Klass* k) const;
+ bool is_same_or_direct_interface(Klass* k) const;
// Access to the implementor of an interface.
Klass* implementor() const