8026065: InterfaceMethodref for invokespecial must name a direct superinterface
authorhseigel
Tue, 26 Nov 2013 09:52:22 -0500
changeset 21768 b7dba4cde1c6
parent 21767 41eaa9a17059
child 21769 61c68af507c9
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
hotspot/src/share/vm/classfile/verifier.cpp
hotspot/src/share/vm/classfile/verifier.hpp
hotspot/src/share/vm/interpreter/linkResolver.cpp
hotspot/src/share/vm/oops/instanceKlass.cpp
hotspot/src/share/vm/oops/instanceKlass.hpp
--- 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