diff options
Diffstat (limited to 'runtime/jni_internal_test.cc')
-rw-r--r-- | runtime/jni_internal_test.cc | 975 |
1 files changed, 656 insertions, 319 deletions
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc index da3080f218..844d14a063 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -17,6 +17,7 @@ #include "jni_internal.h" #include "common_compiler_test.h" +#include "java_vm_ext.h" #include "mirror/art_method-inl.h" #include "mirror/string-inl.h" #include "scoped_thread_state_change.h" @@ -53,24 +54,15 @@ class JniInternalTest : public CommonCompilerTest { } void ExpectException(jclass exception_class) { - EXPECT_TRUE(env_->ExceptionCheck()); + ScopedObjectAccess soa(env_); + EXPECT_TRUE(env_->ExceptionCheck()) + << PrettyDescriptor(soa.Decode<mirror::Class*>(exception_class)); jthrowable exception = env_->ExceptionOccurred(); EXPECT_NE(nullptr, exception); env_->ExceptionClear(); EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); } - void ExpectClassFound(const char* name) { - EXPECT_NE(env_->FindClass(name), nullptr) << name; - EXPECT_FALSE(env_->ExceptionCheck()) << name; - } - - void ExpectClassNotFound(const char* name) { - EXPECT_EQ(env_->FindClass(name), nullptr) << name; - EXPECT_TRUE(env_->ExceptionCheck()) << name; - env_->ExceptionClear(); - } - void CleanUpJniEnv() { if (aioobe_ != nullptr) { env_->DeleteGlobalRef(aioobe_); @@ -98,6 +90,510 @@ class JniInternalTest : public CommonCompilerTest { return soa.AddLocalReference<jclass>(c); } + void ExpectClassFound(const char* name) { + EXPECT_NE(env_->FindClass(name), nullptr) << name; + EXPECT_FALSE(env_->ExceptionCheck()) << name; + } + + void ExpectClassNotFound(const char* name, bool check_jni, const char* check_jni_msg, + CheckJniAbortCatcher* abort_catcher) { + EXPECT_EQ(env_->FindClass(name), nullptr) << name; + if (!check_jni || check_jni_msg == nullptr) { + EXPECT_TRUE(env_->ExceptionCheck()) << name; + env_->ExceptionClear(); + } else { + abort_catcher->Check(check_jni_msg); + } + } + + void FindClassTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + // Null argument is always an abort. + env_->FindClass(nullptr); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "name == null"); + + // Reference types... + ExpectClassFound("java/lang/String"); + // ...for arrays too, where you must include "L;". + ExpectClassFound("[Ljava/lang/String;"); + // Primitive arrays are okay too, if the primitive type is valid. + ExpectClassFound("[C"); + + // But primitive types aren't allowed... + ExpectClassNotFound("C", check_jni, nullptr, &check_jni_abort_catcher); + ExpectClassNotFound("V", check_jni, nullptr, &check_jni_abort_catcher); + ExpectClassNotFound("K", check_jni, nullptr, &check_jni_abort_catcher); + + if (check_jni) { + // Check JNI will reject invalid class names as aborts but without pending exceptions. + EXPECT_EQ(env_->FindClass("java.lang.String"), nullptr); + EXPECT_FALSE(env_->ExceptionCheck()); + check_jni_abort_catcher.Check("illegal class name 'java.lang.String'"); + + EXPECT_EQ(env_->FindClass("[Ljava.lang.String;"), nullptr); + EXPECT_FALSE(env_->ExceptionCheck()); + check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'"); + } else { + // Without check JNI we're tolerant and replace '.' with '/'. + ExpectClassFound("java.lang.String"); + ExpectClassFound("[Ljava.lang.String;"); + } + + ExpectClassNotFound("Ljava.lang.String;", check_jni, "illegal class name 'Ljava.lang.String;'", + &check_jni_abort_catcher); + ExpectClassNotFound("[java.lang.String", check_jni, "illegal class name '[java.lang.String'", + &check_jni_abort_catcher); + + // You can't include the "L;" in a JNI class descriptor. + ExpectClassNotFound("Ljava/lang/String;", check_jni, "illegal class name 'Ljava/lang/String;'", + &check_jni_abort_catcher); + + // But you must include it for an array of any reference type. + ExpectClassNotFound("[java/lang/String", check_jni, "illegal class name '[java/lang/String'", + &check_jni_abort_catcher); + + ExpectClassNotFound("[K", check_jni, "illegal class name '[K'", &check_jni_abort_catcher); + + // Void arrays aren't allowed. + ExpectClassNotFound("[V", check_jni, "illegal class name '[V'", &check_jni_abort_catcher); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetFieldIdBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jclass c = env_->FindClass("java/lang/String"); + ASSERT_NE(c, nullptr); + + jfieldID fid = env_->GetFieldID(nullptr, "count", "I"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "GetFieldID received NULL jclass" + : "java_class == null"); + fid = env_->GetFieldID(c, nullptr, "I"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "name == null"); + fid = env_->GetFieldID(c, "count", nullptr); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "sig == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetStaticFieldIdBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jclass c = env_->FindClass("java/lang/String"); + ASSERT_NE(c, nullptr); + + jfieldID fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "GetStaticFieldID received NULL jclass" + : "java_class == null"); + fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;"); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "name == null"); + fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr); + EXPECT_EQ(nullptr, fid); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "sig == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetMethodIdBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jmethodID method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "GetMethodID received NULL jclass" + : "java_class == null"); + jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError"); + ASSERT_TRUE(jlnsme != nullptr); + method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "name == null"); + method = env_->GetMethodID(jlnsme, "<init>", nullptr); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "sig == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetStaticMethodIdBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jmethodID method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "GetStaticMethodID received NULL jclass" + : "java_class == null"); + jclass jlstring = env_->FindClass("java/lang/String"); + method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;"); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "name == null"); + method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr); + EXPECT_EQ(nullptr, method); + check_jni_abort_catcher.Check(check_jni ? "non-nullable const char* was NULL" + : "sig == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetFromReflectedField_ToReflectedFieldBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jclass c = env_->FindClass("java/lang/String"); + ASSERT_NE(c, nullptr); + jfieldID fid = env_->GetFieldID(c, "count", "I"); + ASSERT_NE(fid, nullptr); + + // Check class argument for null argument, not checked in non-check JNI. + jobject field = env_->ToReflectedField(nullptr, fid, JNI_FALSE); + if (check_jni) { + EXPECT_EQ(field, nullptr); + check_jni_abort_catcher.Check("ToReflectedField received NULL jclass"); + } else { + EXPECT_NE(field, nullptr); + } + + field = env_->ToReflectedField(c, nullptr, JNI_FALSE); + EXPECT_EQ(field, nullptr); + check_jni_abort_catcher.Check(check_jni ? "jfieldID was NULL" + : "fid == null"); + + fid = env_->FromReflectedField(nullptr); + ASSERT_EQ(fid, nullptr); + check_jni_abort_catcher.Check(check_jni ? "expected non-null java.lang.reflect.Field" + : "jlr_field == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher check_jni_abort_catcher; + + jclass c = env_->FindClass("java/lang/String"); + ASSERT_NE(c, nullptr); + jmethodID mid = env_->GetMethodID(c, "<init>", "()V"); + ASSERT_NE(mid, nullptr); + + // Check class argument for null argument, not checked in non-check JNI. + jobject method = env_->ToReflectedMethod(nullptr, mid, JNI_FALSE); + if (check_jni) { + EXPECT_EQ(method, nullptr); + check_jni_abort_catcher.Check("ToReflectedMethod received NULL jclass"); + } else { + EXPECT_NE(method, nullptr); + } + + method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE); + EXPECT_EQ(method, nullptr); + check_jni_abort_catcher.Check(check_jni ? "jmethodID was NULL" + : "mid == null"); + mid = env_->FromReflectedMethod(method); + ASSERT_EQ(mid, nullptr); + check_jni_abort_catcher.Check(check_jni ? "expected non-null method" : "jlr_method == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void RegisterAndUnregisterNativesBadArguments(bool check_jni, + CheckJniAbortCatcher* check_jni_abort_catcher) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + // Passing a class of null is a failure. + { + JNINativeMethod methods[] = { }; + EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR); + check_jni_abort_catcher->Check(check_jni ? "RegisterNatives received NULL jclass" + : "java_class == null"); + } + + // Passing methods as null is a failure. + jclass jlobject = env_->FindClass("java/lang/Object"); + EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR); + check_jni_abort_catcher->Check("methods == null"); + + // Unregisters null is a failure. + EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR); + check_jni_abort_catcher->Check(check_jni ? "UnregisterNatives received NULL jclass" + : "java_class == null"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + + void GetPrimitiveArrayElementsOfWrongType(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + + jbooleanArray array = env_->NewBooleanArray(10); + jboolean is_copy; + EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected byte[]" + : "attempt to get byte primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected short[]" + : "attempt to get short primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected char[]" + : "attempt to get char primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected int[]" + : "attempt to get int primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected long[]" + : "attempt to get long primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected float[]" + : "attempt to get float primitive array elements with an object of type boolean[]"); + EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected double[]" + : "attempt to get double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy), + nullptr); + jni_abort_catcher.Check( + check_jni ? "incompatible array type byte[] expected boolean[]" + : "attempt to get boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy), + nullptr); + jni_abort_catcher.Check( + check_jni ? "jarray argument has non-array type: java.lang.String" + : "attempt to get boolean primitive array elements with an object of type java.lang.String"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void ReleasePrimitiveArrayElementsOfWrongType(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + + jbooleanArray array = env_->NewBooleanArray(10); + ASSERT_TRUE(array != nullptr); + jboolean is_copy; + jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy); + ASSERT_TRUE(elements != nullptr); + env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array), + reinterpret_cast<jbyte*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected byte[]" + : "attempt to release byte primitive array elements with an object of type boolean[]"); + env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array), + reinterpret_cast<jshort*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected short[]" + : "attempt to release short primitive array elements with an object of type boolean[]"); + env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array), + reinterpret_cast<jchar*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected char[]" + : "attempt to release char primitive array elements with an object of type boolean[]"); + env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array), + reinterpret_cast<jint*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected int[]" + : "attempt to release int primitive array elements with an object of type boolean[]"); + env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), + reinterpret_cast<jlong*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected long[]" + : "attempt to release long primitive array elements with an object of type boolean[]"); + env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array), + reinterpret_cast<jfloat*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected float[]" + : "attempt to release float primitive array elements with an object of type boolean[]"); + env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), + reinterpret_cast<jdouble*>(elements), 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected double[]" + : "attempt to release double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0); + jni_abort_catcher.Check( + check_jni ? "incompatible array type byte[] expected boolean[]" + : "attempt to release boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0); + jni_abort_catcher.Check( + check_jni ? "jarray argument has non-array type: java.lang.String" + : "attempt to release boolean primitive array elements with an object of type " + "java.lang.String"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetReleasePrimitiveArrayCriticalOfWrongType(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + + jobject object = env_->NewStringUTF("Test String"); + jboolean is_copy; + void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy); + jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String" + : "expected primitive array, given java.lang.String"); + env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0); + jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String" + : "expected primitive array, given java.lang.String"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void GetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + constexpr size_t kLength = 10; + jbooleanArray array = env_->NewBooleanArray(kLength); + ASSERT_TRUE(array != nullptr); + jboolean elements[kLength]; + env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, + reinterpret_cast<jbyte*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected byte[]" + : "attempt to get region of byte primitive array elements with an object of type boolean[]"); + env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, + reinterpret_cast<jshort*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected short[]" + : "attempt to get region of short primitive array elements with an object of type boolean[]"); + env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, + reinterpret_cast<jchar*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected char[]" + : "attempt to get region of char primitive array elements with an object of type boolean[]"); + env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, + reinterpret_cast<jint*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected int[]" + : "attempt to get region of int primitive array elements with an object of type boolean[]"); + env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, + reinterpret_cast<jlong*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected long[]" + : "attempt to get region of long primitive array elements with an object of type boolean[]"); + env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, + reinterpret_cast<jfloat*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected float[]" + : "attempt to get region of float primitive array elements with an object of type boolean[]"); + env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, + reinterpret_cast<jdouble*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected double[]" + : "attempt to get region of double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type byte[] expected boolean[]" + : "attempt to get region of boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String" + : "attempt to get region of boolean primitive array elements with an object of type " + "java.lang.String"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void SetPrimitiveArrayRegionElementsOfWrongType(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + constexpr size_t kLength = 10; + jbooleanArray array = env_->NewBooleanArray(kLength); + ASSERT_TRUE(array != nullptr); + jboolean elements[kLength]; + env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, + reinterpret_cast<jbyte*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected byte[]" + : "attempt to set region of byte primitive array elements with an object of type boolean[]"); + env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, + reinterpret_cast<jshort*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected short[]" + : "attempt to set region of short primitive array elements with an object of type boolean[]"); + env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, + reinterpret_cast<jchar*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected char[]" + : "attempt to set region of char primitive array elements with an object of type boolean[]"); + env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, + reinterpret_cast<jint*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected int[]" + : "attempt to set region of int primitive array elements with an object of type boolean[]"); + env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, + reinterpret_cast<jlong*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected long[]" + : "attempt to set region of long primitive array elements with an object of type boolean[]"); + env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, + reinterpret_cast<jfloat*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected float[]" + : "attempt to set region of float primitive array elements with an object of type boolean[]"); + env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, + reinterpret_cast<jdouble*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type boolean[] expected double[]" + : "attempt to set region of double primitive array elements with an object of type boolean[]"); + jbyteArray array2 = env_->NewByteArray(10); + env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check( + check_jni ? "incompatible array type byte[] expected boolean[]" + : "attempt to set region of boolean primitive array elements with an object of type byte[]"); + jobject object = env_->NewStringUTF("Test String"); + env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, + reinterpret_cast<jboolean*>(elements)); + jni_abort_catcher.Check(check_jni ? "jarray argument has non-array type: java.lang.String" + : "attempt to set region of boolean primitive array elements with an object of type " + "java.lang.String"); + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + + void NewObjectArrayBadArguments(bool check_jni) { + bool old_check_jni = vm_->SetCheckJniEnabled(check_jni); + CheckJniAbortCatcher jni_abort_catcher; + + jclass element_class = env_->FindClass("java/lang/String"); + ASSERT_NE(element_class, nullptr); + + env_->NewObjectArray(-1, element_class, nullptr); + jni_abort_catcher.Check(check_jni ? "negative jsize: -1" : "negative array length: -1"); + + env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr); + jni_abort_catcher.Check(check_jni ? "negative jsize: -2147483648" + : "negative array length: -2147483648"); + + EXPECT_EQ(check_jni, vm_->SetCheckJniEnabled(old_check_jni)); + } + JavaVMExt* vm_; JNIEnv* env_; jclass aioobe_; @@ -125,48 +621,8 @@ TEST_F(JniInternalTest, GetVersion) { } TEST_F(JniInternalTest, FindClass) { - // Reference types... - ExpectClassFound("java/lang/String"); - // ...for arrays too, where you must include "L;". - ExpectClassFound("[Ljava/lang/String;"); - // Primitive arrays are okay too, if the primitive type is valid. - ExpectClassFound("[C"); - - { - CheckJniAbortCatcher check_jni_abort_catcher; - env_->FindClass(nullptr); - check_jni_abort_catcher.Check("name == null"); - - // We support . as well as / for compatibility, if -Xcheck:jni is off. - ExpectClassFound("java.lang.String"); - check_jni_abort_catcher.Check("illegal class name 'java.lang.String'"); - ExpectClassNotFound("Ljava.lang.String;"); - check_jni_abort_catcher.Check("illegal class name 'Ljava.lang.String;'"); - ExpectClassFound("[Ljava.lang.String;"); - check_jni_abort_catcher.Check("illegal class name '[Ljava.lang.String;'"); - ExpectClassNotFound("[java.lang.String"); - check_jni_abort_catcher.Check("illegal class name '[java.lang.String'"); - - // You can't include the "L;" in a JNI class descriptor. - ExpectClassNotFound("Ljava/lang/String;"); - check_jni_abort_catcher.Check("illegal class name 'Ljava/lang/String;'"); - - // But you must include it for an array of any reference type. - ExpectClassNotFound("[java/lang/String"); - check_jni_abort_catcher.Check("illegal class name '[java/lang/String'"); - - ExpectClassNotFound("[K"); - check_jni_abort_catcher.Check("illegal class name '[K'"); - - // Void arrays aren't allowed. - ExpectClassNotFound("[V"); - check_jni_abort_catcher.Check("illegal class name '[V'"); - } - - // But primitive types aren't allowed... - ExpectClassNotFound("C"); - ExpectClassNotFound("V"); - ExpectClassNotFound("K"); + FindClassTest(false); + FindClassTest(true); } TEST_F(JniInternalTest, GetFieldID) { @@ -208,16 +664,8 @@ TEST_F(JniInternalTest, GetFieldID) { ExpectException(jlnsfe); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - fid = env_->GetFieldID(nullptr, "count", "I"); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("java_class == null"); - fid = env_->GetFieldID(c, nullptr, "I"); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("name == null"); - fid = env_->GetFieldID(c, "count", nullptr); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("sig == null"); + GetFieldIdBadArgumentTest(false); + GetFieldIdBadArgumentTest(true); } TEST_F(JniInternalTest, GetStaticFieldID) { @@ -253,16 +701,8 @@ TEST_F(JniInternalTest, GetStaticFieldID) { ExpectException(jlnsfe); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - fid = env_->GetStaticFieldID(nullptr, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;"); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("java_class == null"); - fid = env_->GetStaticFieldID(c, nullptr, "Ljava/util/Comparator;"); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("name == null"); - fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", nullptr); - EXPECT_EQ(nullptr, fid); - check_jni_abort_catcher.Check("sig == null"); + GetStaticFieldIdBadArgumentTest(false); + GetStaticFieldIdBadArgumentTest(true); } TEST_F(JniInternalTest, GetMethodID) { @@ -302,16 +742,8 @@ TEST_F(JniInternalTest, GetMethodID) { EXPECT_FALSE(env_->ExceptionCheck()); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - method = env_->GetMethodID(nullptr, "<init>", "(Ljava/lang/String;)V"); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("java_class == null"); - method = env_->GetMethodID(jlnsme, nullptr, "(Ljava/lang/String;)V"); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("name == null"); - method = env_->GetMethodID(jlnsme, "<init>", nullptr); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("sig == null"); + GetMethodIdBadArgumentTest(false); + GetMethodIdBadArgumentTest(true); } TEST_F(JniInternalTest, GetStaticMethodID) { @@ -340,16 +772,8 @@ TEST_F(JniInternalTest, GetStaticMethodID) { EXPECT_FALSE(env_->ExceptionCheck()); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - method = env_->GetStaticMethodID(nullptr, "valueOf", "(I)Ljava/lang/String;"); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("java_class == null"); - method = env_->GetStaticMethodID(jlstring, nullptr, "(I)Ljava/lang/String;"); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("name == null"); - method = env_->GetStaticMethodID(jlstring, "valueOf", nullptr); - EXPECT_EQ(nullptr, method); - check_jni_abort_catcher.Check("sig == null"); + GetStaticMethodIdBadArgumentTest(false); + GetStaticMethodIdBadArgumentTest(true); } TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { @@ -370,13 +794,8 @@ TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) { ASSERT_EQ(4, env_->GetIntField(s, fid2)); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - field = env_->ToReflectedField(c, nullptr, JNI_FALSE); - EXPECT_EQ(field, nullptr); - check_jni_abort_catcher.Check("fid == null"); - fid2 = env_->FromReflectedField(nullptr); - ASSERT_EQ(fid2, nullptr); - check_jni_abort_catcher.Check("jlr_field == null"); + GetFromReflectedField_ToReflectedFieldBadArgumentTest(false); + GetFromReflectedField_ToReflectedFieldBadArgumentTest(true); } TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) { @@ -417,13 +836,8 @@ TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) { ASSERT_EQ(4, env_->CallIntMethod(s, mid2)); // Bad arguments. - CheckJniAbortCatcher check_jni_abort_catcher; - method = env_->ToReflectedMethod(c, nullptr, JNI_FALSE); - EXPECT_EQ(method, nullptr); - check_jni_abort_catcher.Check("mid == null"); - mid2 = env_->FromReflectedMethod(method); - ASSERT_EQ(mid2, nullptr); - check_jni_abort_catcher.Check("jlr_method == null"); + GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(false); + GetFromReflectedMethod_ToReflectedMethodBadArgumentTest(true); } static void BogusMethod() { @@ -498,23 +912,11 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) { } EXPECT_FALSE(env_->ExceptionCheck()); - // Passing a class of null is a failure. - { - JNINativeMethod methods[] = { }; - EXPECT_EQ(env_->RegisterNatives(nullptr, methods, 0), JNI_ERR); - check_jni_abort_catcher.Check("java_class == null"); - } - - // Passing methods as null is a failure. - EXPECT_EQ(env_->RegisterNatives(jlobject, nullptr, 1), JNI_ERR); - check_jni_abort_catcher.Check("methods == null"); - - // Unregisters null is a failure. - EXPECT_EQ(env_->UnregisterNatives(nullptr), JNI_ERR); - check_jni_abort_catcher.Check("java_class == null"); - // Unregistering a class with no natives is a warning. EXPECT_EQ(env_->UnregisterNatives(jlnsme), JNI_OK); + + RegisterAndUnregisterNativesBadArguments(false, &check_jni_abort_catcher); + RegisterAndUnregisterNativesBadArguments(true, &check_jni_abort_catcher); } #define EXPECT_PRIMITIVE_ARRAY(new_fn, \ @@ -528,6 +930,7 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) { \ { \ CheckJniAbortCatcher jni_abort_catcher; \ + down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(false); \ /* Allocate an negative sized array and check it has the right failure type. */ \ EXPECT_EQ(env_->new_fn(-1), nullptr); \ jni_abort_catcher.Check("negative array length: -1"); \ @@ -550,6 +953,7 @@ TEST_F(JniInternalTest, RegisterAndUnregisterNatives) { jni_abort_catcher.Check("buf == null"); \ env_->set_region_fn(a, 0, size, nullptr); \ jni_abort_catcher.Check("buf == null"); \ + down_cast<JNIEnvExt*>(env_)->SetCheckJniEnabled(true); \ } \ /* Allocate an array and check it has the right type and length. */ \ scalar_type ## Array a = env_->new_fn(size); \ @@ -654,189 +1058,28 @@ TEST_F(JniInternalTest, ShortArrays) { } TEST_F(JniInternalTest, GetPrimitiveArrayElementsOfWrongType) { - CheckJniAbortCatcher jni_abort_catcher; - jbooleanArray array = env_->NewBooleanArray(10); - jboolean is_copy; - EXPECT_EQ(env_->GetByteArrayElements(reinterpret_cast<jbyteArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get byte primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetShortArrayElements(reinterpret_cast<jshortArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get short primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetCharArrayElements(reinterpret_cast<jcharArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get char primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetIntArrayElements(reinterpret_cast<jintArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get int primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetLongArrayElements(reinterpret_cast<jlongArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get long primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetFloatArrayElements(reinterpret_cast<jfloatArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get float primitive array elements with an object of type boolean[]"); - EXPECT_EQ(env_->GetDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), &is_copy), nullptr); - jni_abort_catcher.Check( - "attempt to get double primitive array elements with an object of type boolean[]"); - jbyteArray array2 = env_->NewByteArray(10); - EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), &is_copy), - nullptr); - jni_abort_catcher.Check( - "attempt to get boolean primitive array elements with an object of type byte[]"); - jobject object = env_->NewStringUTF("Test String"); - EXPECT_EQ(env_->GetBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), &is_copy), - nullptr); - jni_abort_catcher.Check( - "attempt to get boolean primitive array elements with an object of type java.lang.String"); + GetPrimitiveArrayElementsOfWrongType(false); + GetPrimitiveArrayElementsOfWrongType(true); } TEST_F(JniInternalTest, ReleasePrimitiveArrayElementsOfWrongType) { - CheckJniAbortCatcher jni_abort_catcher; - jbooleanArray array = env_->NewBooleanArray(10); - ASSERT_TRUE(array != nullptr); - jboolean is_copy; - jboolean* elements = env_->GetBooleanArrayElements(array, &is_copy); - ASSERT_TRUE(elements != nullptr); - env_->ReleaseByteArrayElements(reinterpret_cast<jbyteArray>(array), - reinterpret_cast<jbyte*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release byte primitive array elements with an object of type boolean[]"); - env_->ReleaseShortArrayElements(reinterpret_cast<jshortArray>(array), - reinterpret_cast<jshort*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release short primitive array elements with an object of type boolean[]"); - env_->ReleaseCharArrayElements(reinterpret_cast<jcharArray>(array), - reinterpret_cast<jchar*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release char primitive array elements with an object of type boolean[]"); - env_->ReleaseIntArrayElements(reinterpret_cast<jintArray>(array), - reinterpret_cast<jint*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release int primitive array elements with an object of type boolean[]"); - env_->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), - reinterpret_cast<jlong*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release long primitive array elements with an object of type boolean[]"); - env_->ReleaseFloatArrayElements(reinterpret_cast<jfloatArray>(array), - reinterpret_cast<jfloat*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release float primitive array elements with an object of type boolean[]"); - env_->ReleaseDoubleArrayElements(reinterpret_cast<jdoubleArray>(array), - reinterpret_cast<jdouble*>(elements), 0); - jni_abort_catcher.Check( - "attempt to release double primitive array elements with an object of type boolean[]"); - jbyteArray array2 = env_->NewByteArray(10); - env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(array2), elements, 0); - jni_abort_catcher.Check( - "attempt to release boolean primitive array elements with an object of type byte[]"); - jobject object = env_->NewStringUTF("Test String"); - env_->ReleaseBooleanArrayElements(reinterpret_cast<jbooleanArray>(object), elements, 0); - jni_abort_catcher.Check( - "attempt to release boolean primitive array elements with an object of type " - "java.lang.String"); + ReleasePrimitiveArrayElementsOfWrongType(false); + ReleasePrimitiveArrayElementsOfWrongType(true); } + TEST_F(JniInternalTest, GetReleasePrimitiveArrayCriticalOfWrongType) { - CheckJniAbortCatcher jni_abort_catcher; - jobject object = env_->NewStringUTF("Test String"); - jboolean is_copy; - void* elements = env_->GetPrimitiveArrayCritical(reinterpret_cast<jarray>(object), &is_copy); - jni_abort_catcher.Check("expected primitive array, given java.lang.String"); - env_->ReleasePrimitiveArrayCritical(reinterpret_cast<jarray>(object), elements, 0); - jni_abort_catcher.Check("expected primitive array, given java.lang.String"); + GetReleasePrimitiveArrayCriticalOfWrongType(false); + GetReleasePrimitiveArrayCriticalOfWrongType(true); } TEST_F(JniInternalTest, GetPrimitiveArrayRegionElementsOfWrongType) { - CheckJniAbortCatcher jni_abort_catcher; - constexpr size_t kLength = 10; - jbooleanArray array = env_->NewBooleanArray(kLength); - ASSERT_TRUE(array != nullptr); - jboolean elements[kLength]; - env_->GetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, - reinterpret_cast<jbyte*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of byte primitive array elements with an object of type boolean[]"); - env_->GetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, - reinterpret_cast<jshort*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of short primitive array elements with an object of type boolean[]"); - env_->GetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, - reinterpret_cast<jchar*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of char primitive array elements with an object of type boolean[]"); - env_->GetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, - reinterpret_cast<jint*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of int primitive array elements with an object of type boolean[]"); - env_->GetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, - reinterpret_cast<jlong*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of long primitive array elements with an object of type boolean[]"); - env_->GetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, - reinterpret_cast<jfloat*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of float primitive array elements with an object of type boolean[]"); - env_->GetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, - reinterpret_cast<jdouble*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of double primitive array elements with an object of type boolean[]"); - jbyteArray array2 = env_->NewByteArray(10); - env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, - reinterpret_cast<jboolean*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of boolean primitive array elements with an object of type byte[]"); - jobject object = env_->NewStringUTF("Test String"); - env_->GetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, - reinterpret_cast<jboolean*>(elements)); - jni_abort_catcher.Check( - "attempt to get region of boolean primitive array elements with an object of type " - "java.lang.String"); + GetPrimitiveArrayRegionElementsOfWrongType(false); + GetPrimitiveArrayRegionElementsOfWrongType(true); } TEST_F(JniInternalTest, SetPrimitiveArrayRegionElementsOfWrongType) { - CheckJniAbortCatcher jni_abort_catcher; - constexpr size_t kLength = 10; - jbooleanArray array = env_->NewBooleanArray(kLength); - ASSERT_TRUE(array != nullptr); - jboolean elements[kLength]; - env_->SetByteArrayRegion(reinterpret_cast<jbyteArray>(array), 0, kLength, - reinterpret_cast<jbyte*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of byte primitive array elements with an object of type boolean[]"); - env_->SetShortArrayRegion(reinterpret_cast<jshortArray>(array), 0, kLength, - reinterpret_cast<jshort*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of short primitive array elements with an object of type boolean[]"); - env_->SetCharArrayRegion(reinterpret_cast<jcharArray>(array), 0, kLength, - reinterpret_cast<jchar*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of char primitive array elements with an object of type boolean[]"); - env_->SetIntArrayRegion(reinterpret_cast<jintArray>(array), 0, kLength, - reinterpret_cast<jint*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of int primitive array elements with an object of type boolean[]"); - env_->SetLongArrayRegion(reinterpret_cast<jlongArray>(array), 0, kLength, - reinterpret_cast<jlong*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of long primitive array elements with an object of type boolean[]"); - env_->SetFloatArrayRegion(reinterpret_cast<jfloatArray>(array), 0, kLength, - reinterpret_cast<jfloat*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of float primitive array elements with an object of type boolean[]"); - env_->SetDoubleArrayRegion(reinterpret_cast<jdoubleArray>(array), 0, kLength, - reinterpret_cast<jdouble*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of double primitive array elements with an object of type boolean[]"); - jbyteArray array2 = env_->NewByteArray(10); - env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(array2), 0, kLength, - reinterpret_cast<jboolean*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of boolean primitive array elements with an object of type byte[]"); - jobject object = env_->NewStringUTF("Test String"); - env_->SetBooleanArrayRegion(reinterpret_cast<jbooleanArray>(object), 0, kLength, - reinterpret_cast<jboolean*>(elements)); - jni_abort_catcher.Check( - "attempt to set region of boolean primitive array elements with an object of type " - "java.lang.String"); + SetPrimitiveArrayRegionElementsOfWrongType(false); + SetPrimitiveArrayRegionElementsOfWrongType(true); } TEST_F(JniInternalTest, NewObjectArray) { @@ -857,12 +1100,8 @@ TEST_F(JniInternalTest, NewObjectArray) { EXPECT_TRUE(env_->IsSameObject(env_->GetObjectArrayElement(a, 0), nullptr)); // Negative array length checks. - CheckJniAbortCatcher jni_abort_catcher; - env_->NewObjectArray(-1, element_class, nullptr); - jni_abort_catcher.Check("negative array length: -1"); - - env_->NewObjectArray(std::numeric_limits<jint>::min(), element_class, nullptr); - jni_abort_catcher.Check("negative array length: -2147483648"); + NewObjectArrayBadArguments(false); + NewObjectArrayBadArguments(true); } TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { @@ -872,6 +1111,7 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { }; ASSERT_EQ(strlen(primitive_descriptors), arraysize(primitive_names)); + bool old_check_jni = vm_->SetCheckJniEnabled(false); CheckJniAbortCatcher jni_abort_catcher; for (size_t i = 0; i < strlen(primitive_descriptors); ++i) { env_->NewObjectArray(0, nullptr, nullptr); @@ -881,6 +1121,16 @@ TEST_F(JniInternalTest, NewObjectArrayWithPrimitiveClasses) { std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i])); jni_abort_catcher.Check(error_msg.c_str()); } + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + for (size_t i = 0; i < strlen(primitive_descriptors); ++i) { + env_->NewObjectArray(0, nullptr, nullptr); + jni_abort_catcher.Check("NewObjectArray received NULL jclass"); + jclass primitive_class = GetPrimitiveClass(primitive_descriptors[i]); + env_->NewObjectArray(1, primitive_class, nullptr); + std::string error_msg(StringPrintf("not an object type: %s", primitive_names[i])); + jni_abort_catcher.Check(error_msg.c_str()); + } + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } TEST_F(JniInternalTest, NewObjectArrayWithInitialValue) { @@ -940,8 +1190,13 @@ TEST_F(JniInternalTest, GetSuperclass) { // Null as class should fail. CheckJniAbortCatcher jni_abort_catcher; + bool old_check_jni = vm_->SetCheckJniEnabled(false); EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr); jni_abort_catcher.Check("java_class == null"); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + EXPECT_EQ(env_->GetSuperclass(nullptr), nullptr); + jni_abort_catcher.Check("GetSuperclass received NULL jclass"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } TEST_F(JniInternalTest, IsAssignableFrom) { @@ -975,10 +1230,17 @@ TEST_F(JniInternalTest, IsAssignableFrom) { // Null as either class should fail. CheckJniAbortCatcher jni_abort_catcher; + bool old_check_jni = vm_->SetCheckJniEnabled(false); EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE); jni_abort_catcher.Check("java_class1 == null"); EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE); jni_abort_catcher.Check("java_class2 == null"); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + EXPECT_EQ(env_->IsAssignableFrom(nullptr, string_class), JNI_FALSE); + jni_abort_catcher.Check("IsAssignableFrom received NULL jclass"); + EXPECT_EQ(env_->IsAssignableFrom(object_class, nullptr), JNI_FALSE); + jni_abort_catcher.Check("IsAssignableFrom received NULL jclass"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } TEST_F(JniInternalTest, GetObjectRefType) { @@ -1063,10 +1325,17 @@ TEST_F(JniInternalTest, NewStringNullCharsNonzeroLength) { TEST_F(JniInternalTest, NewStringNegativeLength) { CheckJniAbortCatcher jni_abort_catcher; + bool old_check_jni = vm_->SetCheckJniEnabled(false); env_->NewString(nullptr, -1); jni_abort_catcher.Check("char_count < 0: -1"); env_->NewString(nullptr, std::numeric_limits<jint>::min()); jni_abort_catcher.Check("char_count < 0: -2147483648"); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + env_->NewString(nullptr, -1); + jni_abort_catcher.Check("negative jsize: -1"); + env_->NewString(nullptr, std::numeric_limits<jint>::min()); + jni_abort_catcher.Check("negative jsize: -2147483648"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } TEST_F(JniInternalTest, GetStringLength_GetStringUTFLength) { @@ -1124,10 +1393,17 @@ TEST_F(JniInternalTest, GetStringRegion_GetStringUTFRegion) { TEST_F(JniInternalTest, GetStringUTFChars_ReleaseStringUTFChars) { // Passing in a nullptr jstring is ignored normally, but caught by -Xcheck:jni. + bool old_check_jni = vm_->SetCheckJniEnabled(false); { CheckJniAbortCatcher check_jni_abort_catcher; EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr); - check_jni_abort_catcher.Check("GetStringUTFChars received null jstring"); + } + { + CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + EXPECT_EQ(env_->GetStringUTFChars(nullptr, nullptr), nullptr); + check_jni_abort_catcher.Check("GetStringUTFChars received NULL jstring"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } jstring s = env_->NewStringUTF("hello"); @@ -1222,10 +1498,17 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { // Null as array should fail. CheckJniAbortCatcher jni_abort_catcher; + bool old_check_jni = vm_->SetCheckJniEnabled(false); EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0)); jni_abort_catcher.Check("java_array == null"); env_->SetObjectArrayElement(nullptr, 0, nullptr); jni_abort_catcher.Check("java_array == null"); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + EXPECT_EQ(nullptr, env_->GetObjectArrayElement(nullptr, 0)); + jni_abort_catcher.Check("jarray was NULL"); + env_->SetObjectArrayElement(nullptr, 0, nullptr); + jni_abort_catcher.Check("jarray was NULL"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } #define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \ @@ -1237,15 +1520,28 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { env_->SetStatic ## type ## Field(c, fid, value2); \ EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \ \ + bool old_check_jni = vm_->SetCheckJniEnabled(false); \ + { \ + CheckJniAbortCatcher jni_abort_catcher; \ + env_->GetStatic ## type ## Field(nullptr, fid); \ + env_->SetStatic ## type ## Field(nullptr, fid, value1); \ + } \ CheckJniAbortCatcher jni_abort_catcher; \ - env_->GetStatic ## type ## Field(nullptr, fid); \ - jni_abort_catcher.Check("received null jclass"); \ - env_->SetStatic ## type ## Field(nullptr, fid, value1); \ - jni_abort_catcher.Check("received null jclass"); \ env_->GetStatic ## type ## Field(c, nullptr); \ jni_abort_catcher.Check("fid == null"); \ env_->SetStatic ## type ## Field(c, nullptr, value1); \ jni_abort_catcher.Check("fid == null"); \ + \ + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \ + env_->GetStatic ## type ## Field(nullptr, fid); \ + jni_abort_catcher.Check("received NULL jclass"); \ + env_->SetStatic ## type ## Field(nullptr, fid, value1); \ + jni_abort_catcher.Check("received NULL jclass"); \ + env_->GetStatic ## type ## Field(c, nullptr); \ + jni_abort_catcher.Check("jfieldID was NULL"); \ + env_->SetStatic ## type ## Field(c, nullptr, value1); \ + jni_abort_catcher.Check("jfieldID was NULL"); \ + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \ } while (false) #define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \ @@ -1257,6 +1553,7 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { env_->Set ## type ## Field(instance, fid, value2); \ EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \ \ + bool old_check_jni = vm_->SetCheckJniEnabled(false); \ CheckJniAbortCatcher jni_abort_catcher; \ env_->Get ## type ## Field(nullptr, fid); \ jni_abort_catcher.Check("obj == null"); \ @@ -1266,6 +1563,16 @@ TEST_F(JniInternalTest, GetObjectArrayElement_SetObjectArrayElement) { jni_abort_catcher.Check("fid == null"); \ env_->Set ## type ## Field(instance, nullptr, value1); \ jni_abort_catcher.Check("fid == null"); \ + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); \ + env_->Get ## type ## Field(nullptr, fid); \ + jni_abort_catcher.Check("field operation on NULL object:"); \ + env_->Set ## type ## Field(nullptr, fid, value1); \ + jni_abort_catcher.Check("field operation on NULL object:"); \ + env_->Get ## type ## Field(instance, nullptr); \ + jni_abort_catcher.Check("jfieldID was NULL"); \ + env_->Set ## type ## Field(instance, nullptr, value1); \ + jni_abort_catcher.Check("jfieldID was NULL"); \ + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); \ } while (false) @@ -1357,12 +1664,17 @@ TEST_F(JniInternalTest, DeleteLocalRef) { // Currently, deleting an already-deleted reference is just a CheckJNI warning. { + bool old_check_jni = vm_->SetCheckJniEnabled(false); + { + CheckJniAbortCatcher check_jni_abort_catcher; + env_->DeleteLocalRef(s); + } CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); env_->DeleteLocalRef(s); - - std::string expected(StringPrintf("native code passing in reference to " - "invalid local reference: %p", s)); + std::string expected(StringPrintf("jobject is an invalid local reference: %p", s)); check_jni_abort_catcher.Check(expected.c_str()); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } s = env_->NewStringUTF(""); @@ -1453,12 +1765,17 @@ TEST_F(JniInternalTest, DeleteGlobalRef) { // Currently, deleting an already-deleted reference is just a CheckJNI warning. { + bool old_check_jni = vm_->SetCheckJniEnabled(false); + { + CheckJniAbortCatcher check_jni_abort_catcher; + env_->DeleteGlobalRef(o); + } CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); env_->DeleteGlobalRef(o); - - std::string expected(StringPrintf("native code passing in reference to " - "invalid global reference: %p", o)); + std::string expected(StringPrintf("jobject is an invalid global reference: %p", o)); check_jni_abort_catcher.Check(expected.c_str()); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } jobject o1 = env_->NewGlobalRef(s); @@ -1498,12 +1815,17 @@ TEST_F(JniInternalTest, DeleteWeakGlobalRef) { // Currently, deleting an already-deleted reference is just a CheckJNI warning. { + bool old_check_jni = vm_->SetCheckJniEnabled(false); + { + CheckJniAbortCatcher check_jni_abort_catcher; + env_->DeleteWeakGlobalRef(o); + } CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); env_->DeleteWeakGlobalRef(o); - - std::string expected(StringPrintf("native code passing in reference to " - "invalid weak global reference: %p", o)); + std::string expected(StringPrintf("jobject is an invalid weak global reference: %p", o)); check_jni_abort_catcher.Check(expected.c_str()); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } jobject o1 = env_->NewWeakGlobalRef(s); @@ -1522,8 +1844,6 @@ TEST_F(JniInternalTest, ExceptionDescribe) { } TEST_F(JniInternalTest, Throw) { - EXPECT_EQ(JNI_ERR, env_->Throw(nullptr)); - jclass exception_class = env_->FindClass("java/lang/RuntimeException"); ASSERT_TRUE(exception_class != nullptr); jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class)); @@ -1534,11 +1854,18 @@ TEST_F(JniInternalTest, Throw) { jthrowable thrown_exception = env_->ExceptionOccurred(); env_->ExceptionClear(); EXPECT_TRUE(env_->IsSameObject(exception, thrown_exception)); -} -TEST_F(JniInternalTest, ThrowNew) { + // Bad argument. + bool old_check_jni = vm_->SetCheckJniEnabled(false); EXPECT_EQ(JNI_ERR, env_->Throw(nullptr)); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_EQ(JNI_ERR, env_->Throw(nullptr)); + check_jni_abort_catcher.Check("Throw received NULL jthrowable"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); +} +TEST_F(JniInternalTest, ThrowNew) { jclass exception_class = env_->FindClass("java/lang/RuntimeException"); ASSERT_TRUE(exception_class != nullptr); @@ -1555,6 +1882,16 @@ TEST_F(JniInternalTest, ThrowNew) { thrown_exception = env_->ExceptionOccurred(); env_->ExceptionClear(); EXPECT_TRUE(env_->IsInstanceOf(thrown_exception, exception_class)); + + // Bad argument. + bool old_check_jni = vm_->SetCheckJniEnabled(false); + CheckJniAbortCatcher check_jni_abort_catcher; + EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr)); + check_jni_abort_catcher.Check("c == null"); + EXPECT_FALSE(vm_->SetCheckJniEnabled(true)); + EXPECT_EQ(JNI_ERR, env_->ThrowNew(nullptr, nullptr)); + check_jni_abort_catcher.Check("ThrowNew received NULL jclass"); + EXPECT_TRUE(vm_->SetCheckJniEnabled(old_check_jni)); } TEST_F(JniInternalTest, NewDirectBuffer_GetDirectBufferAddress_GetDirectBufferCapacity) { |