Workaround invokesuper underspecified behavior.
The verifier allows invokesuper on a class unrelated
to the referring class. However, the runtime uses the vtable of
the super class of the referring class to lookup the ArtMethod.
Since the receiver has no relation to the referring class, this lead
to either jumping to a wrong method, or "luckily" throw a
NoSuchMethodError if the vtable index is out of bounds of the super
class of the referring class.
This changes the runtime behavior to always throw NoSuchMethodError
when hitting such invokesuper.
Also, we make the verifier consistent with the runtime by treating
such calls unresolved.
bug=27627004
Change-Id: I68486501a9625f91679078c5a980b39974ddbf1c
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 16fbfaa..fc62573 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -514,12 +514,18 @@
CHECK(self->IsExceptionPending());
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referring_class->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we throw
+ // a NoSuchMethodError.
+ mirror::Class* super_class = nullptr;
+ if (method_reference_class->IsAssignableFrom(referring_class)) {
+ super_class = referring_class->GetSuperClass();
+ }
uint16_t vtable_index = resolved_method->GetMethodIndex();
if (access_check) {
// Check existence of super class.
- if (super_class == nullptr || !super_class->HasVTable() ||
+ if (super_class == nullptr ||
+ !super_class->HasVTable() ||
vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) {
// Behavior to agree with that of the verifier.
ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
@@ -693,8 +699,13 @@
// Need to do full type resolution...
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we cannot
+ // resolve the method.
+ if (!method_reference_class->IsAssignableFrom(referring_class)) {
+ return nullptr;
+ }
+ mirror::Class* super_class = referring_class->GetSuperClass();
if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
// The super class does not have the method.
return nullptr;