More checks in JNI RegisterNatives

Throws NoSuchMethodError (and returns JNI_ERR) when given method name, method
signature or native function is null.

Bug: https://code.google.com/p/android/issues/detail?id=72293
Bug: 15886341
Change-Id: I1c0582d54031eaa58a6025a2417d65090a2a622a
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 0624281..845691d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -112,6 +112,17 @@
                                  kind, c->GetDescriptor().c_str(), name, sig);
 }
 
+static void ReportInvalidJNINativeMethod(const ScopedObjectAccess& soa, mirror::Class* c,
+                                         const char* kind, jint idx, bool return_errors)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  LOG(return_errors ? ERROR : FATAL) << "Failed to register native method in "
+      << PrettyDescriptor(c) << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8()
+      << ": " << kind << " is null at index " << idx;
+  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
+  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchMethodError;",
+                                 "%s is null at index %d", kind, idx);
+}
+
 static mirror::Class* EnsureInitialized(Thread* self, mirror::Class* klass)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (LIKELY(klass->IsInitialized())) {
@@ -2357,6 +2368,17 @@
     for (jint i = 0; i < method_count; ++i) {
       const char* name = methods[i].name;
       const char* sig = methods[i].signature;
+      const void* fnPtr = methods[i].fnPtr;
+      if (UNLIKELY(name == nullptr)) {
+        ReportInvalidJNINativeMethod(soa, c, "method name", i, return_errors);
+        return JNI_ERR;
+      } else if (UNLIKELY(sig == nullptr)) {
+        ReportInvalidJNINativeMethod(soa, c, "method signature", i, return_errors);
+        return JNI_ERR;
+      } else if (UNLIKELY(fnPtr == nullptr)) {
+        ReportInvalidJNINativeMethod(soa, c, "native function", i, return_errors);
+        return JNI_ERR;
+      }
       bool is_fast = false;
       if (*sig == '!') {
         is_fast = true;
@@ -2384,7 +2406,7 @@
 
       VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
 
-      m->RegisterNative(soa.Self(), methods[i].fnPtr, is_fast);
+      m->RegisterNative(soa.Self(), fnPtr, is_fast);
     }
     return JNI_OK;
   }
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index d50e094..8ef1cb6 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -432,27 +432,49 @@
 TEST_F(JniInternalTest, RegisterAndUnregisterNatives) {
   jclass jlobject = env_->FindClass("java/lang/Object");
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
+  void* native_function = reinterpret_cast<void*>(BogusMethod);
 
   // Sanity check that no exceptions are pending.
   ASSERT_FALSE(env_->ExceptionCheck());
 
+  // Check that registering method without name causes a NoSuchMethodError.
+  {
+    JNINativeMethod methods[] = { { nullptr, "()V", native_function } };
+    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+  }
+  ExpectException(jlnsme);
+
+  // Check that registering method without signature causes a NoSuchMethodError.
+  {
+    JNINativeMethod methods[] = { { "notify", nullptr, native_function } };
+    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+  }
+  ExpectException(jlnsme);
+
+  // Check that registering method without function causes a NoSuchMethodError.
+  {
+    JNINativeMethod methods[] = { { "notify", "()V", nullptr } };
+    EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
+  }
+  ExpectException(jlnsme);
+
   // Check that registering to a non-existent java.lang.Object.foo() causes a NoSuchMethodError.
   {
-    JNINativeMethod methods[] = { { "foo", "()V", nullptr } };
+    JNINativeMethod methods[] = { { "foo", "()V", native_function } };
     EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
   }
   ExpectException(jlnsme);
 
   // Check that registering non-native methods causes a NoSuchMethodError.
   {
-    JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", nullptr } };
+    JNINativeMethod methods[] = { { "equals", "(Ljava/lang/Object;)Z", native_function } };
     EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_ERR);
   }
   ExpectException(jlnsme);
 
   // Check that registering native methods is successful.
   {
-    JNINativeMethod methods[] = { { "notify", "()V", reinterpret_cast<void*>(BogusMethod) } };
+    JNINativeMethod methods[] = { { "notify", "()V", native_function } };
     EXPECT_EQ(env_->RegisterNatives(jlobject, methods, 1), JNI_OK);
   }
   EXPECT_FALSE(env_->ExceptionCheck());