Add descriptor validation to ClassLinker::FindClass().
And add tests for odd signatures passed to JNI GetFieldID().
Previously, passing the signature "java/lang/String" instead
of "Ljava/lang/String;" would call the class loader with the
dot name java.lang.String and the class loader would find
that class only to fail the DescriptorEquals() check back
in ClassLinker::FindClass().
Test: 647-jni-get-field-id
Bug: 33577836
Bug: 37156832
Change-Id: I6612a272ec24b0d54b728fd35003e9c24a7e2e95
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 4bc8e8e..1d06842 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2592,9 +2592,25 @@
return nullptr;
}
+ // Inlined DescriptorToDot(descriptor) with extra validation.
+ //
+ // Throw NoClassDefFoundError early rather than potentially load a class only to fail
+ // the DescriptorEquals() check below and give a confusing error message. For example,
+ // when native code erroneously calls JNI GetFieldId() with signature "java/lang/String"
+ // instead of "Ljava/lang/String;", the message below using the "dot" names would be
+ // "class loader [...] returned class java.lang.String instead of java.lang.String".
+ size_t descriptor_length = strlen(descriptor);
+ if (UNLIKELY(descriptor[0] != 'L') ||
+ UNLIKELY(descriptor[descriptor_length - 1] != ';') ||
+ UNLIKELY(memchr(descriptor + 1, '.', descriptor_length - 2) != nullptr)) {
+ ThrowNoClassDefFoundError("Invalid descriptor: %s.", descriptor);
+ return nullptr;
+ }
+ std::string class_name_string(descriptor + 1, descriptor_length - 2);
+ std::replace(class_name_string.begin(), class_name_string.end(), '/', '.');
+
ScopedLocalRef<jobject> class_loader_object(
soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
- std::string class_name_string(DescriptorToDot(descriptor));
ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);