From 42ef8ab151a3d0cbb42cb43f6841c3708d65fca3 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 3 Dec 2015 17:27:32 -0800 Subject: ART: Stash a resolved method late in the verifier Invoke-interface should only be called on an interface method. We cannot move the check earlier, as there are other checks that must be done that can fail a class hard. So postpone a push to the dex cache. Clean up the test a bit. Also templatize ResolveMethod with a version always checking the invoke type, and on a cache miss check whether type target type is an interface when an interface invoke type was given. Bug: 21869691 Change-Id: I94cbb23339cbbb3cb6be9995775e4dcefacce7fd --- runtime/class_linker.cc | 55 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) (limited to 'runtime/class_linker.cc') diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0d10b4e567..f5085ed417 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6162,6 +6162,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, return resolved; } +template ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t method_idx, Handle dex_cache, @@ -6173,6 +6174,12 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); if (resolved != nullptr && !resolved->IsRuntimeMethod()) { DCHECK(resolved->GetDeclaringClassUnchecked() != nullptr) << resolved->GetDexMethodIndex(); + if (kResolveMode == ClassLinker::kForceICCECheck) { + if (resolved->CheckIncompatibleClassChange(type)) { + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer); + return nullptr; + } + } return resolved; } // Fail, get the declaring class. @@ -6191,8 +6198,36 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); break; case kInterface: - resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); - DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); + // We have to check whether the method id really belongs to an interface (dex static bytecode + // constraint A15). Otherwise you must not invoke-interface on it. + // + // This is not symmetric to A12-A14 (direct, static, virtual), as using FindInterfaceMethod + // assumes that the given type is an interface, and will check the interface table if the + // method isn't declared in the class. So it may find an interface method (usually by name + // in the handling below, but we do the constraint check early). In that case, + // CheckIncompatibleClassChange will succeed (as it is called on an interface method) + // unexpectedly. + // Example: + // interface I { + // foo() + // } + // class A implements I { + // ... + // } + // class B extends A { + // ... + // } + // invoke-interface B.foo + // -> FindInterfaceMethod finds I.foo (interface method), not A.foo (miranda method) + if (UNLIKELY(!klass->IsInterface())) { + ThrowIncompatibleClassChangeError(klass, + "Found class %s, but interface was expected", + PrettyDescriptor(klass).c_str()); + return nullptr; + } else { + resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx, image_pointer_size_); + DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); + } break; case kSuper: // Fall-through. case kVirtual: @@ -6794,4 +6829,20 @@ void ClassLinker::CleanupClassLoaders() { } } +// Instantiate ResolveMethod. +template ArtMethod* ClassLinker::ResolveMethod( + const DexFile& dex_file, + uint32_t method_idx, + Handle dex_cache, + Handle class_loader, + ArtMethod* referrer, + InvokeType type); +template ArtMethod* ClassLinker::ResolveMethod( + const DexFile& dex_file, + uint32_t method_idx, + Handle dex_cache, + Handle class_loader, + ArtMethod* referrer, + InvokeType type); + } // namespace art -- cgit v1.2.3-59-g8ed1b