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);