/* * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// Heap objects are allowed to be directly referenced only in VM code, // not in native code.
#define ASSERT_OOPS_ALLOWED \
assert(JavaThread::current()->thread_state() == _thread_in_vm, \ "jniCheck examining oops in bad state.")
// Execute the given block of source code with the thread in VM state. // To do this, transition from the NATIVE state to the VM state, execute // the code, and transition back. The ThreadInVMfromNative constructor // performs the transition to VM state, its destructor restores the // NATIVE state.
// All JNI checked functions here use JNI_ENTRY_CHECKED() instead of the // QUICK_ENTRY or LEAF variants found in jni.cpp. This allows handles // to be created if a fatal error should occur.
// Check for thread not attached to VM; need to catch this before // assertions in the wrapper routines might fire
// Check for env being the one value appropriate for this thread.
staticconstchar * warn_wrong_jnienv = "Using JNIEnv in the wrong thread"; staticconstchar * warn_bad_class_descriptor1 = "JNI FindClass received a bad class descriptor \""; staticconstchar * warn_bad_class_descriptor2 = "\". A correct class descriptor " \ "has no leading \"L\" or trailing \";\". Incorrect descriptors will not be accepted in future releases."; staticconstchar * fatal_using_jnienv_in_nonjava = "FATAL ERROR in native method: Using JNIEnv in non-Java thread"; staticconstchar * warn_other_function_in_critical = "Warning: Calling other JNI functions in the scope of " \ "Get/ReleasePrimitiveArrayCritical or Get/ReleaseStringCritical"; staticconstchar * fatal_bad_ref_to_jni = "Bad global or local ref passed to JNI"; staticconstchar * fatal_received_null_class = "JNI received a null class"; staticconstchar * fatal_class_not_a_class = "JNI received a class argument that is not a class"; staticconstchar * fatal_class_not_a_throwable_class = "JNI Throw or ThrowNew received a class argument that is not a Throwable or Throwable subclass"; staticconstchar * fatal_wrong_class_or_method = "Wrong object class or methodID passed to JNI call"; staticconstchar * fatal_non_weak_method = "non-weak methodID passed to JNI call"; staticconstchar * fatal_unknown_array_object = "Unknown array object passed to JNI array operations"; staticconstchar * fatal_object_array_expected = "Object array expected but not received for JNI array operation"; staticconstchar * fatal_prim_type_array_expected = "Primitive type array expected but not received for JNI array operation"; staticconstchar * fatal_non_array = "Non-array passed to JNI array operations"; staticconstchar * fatal_element_type_mismatch = "Array element type mismatch in JNI"; staticconstchar * fatal_should_be_static = "Non-static field ID passed to JNI"; staticconstchar * fatal_wrong_static_field = "Wrong static field ID passed to JNI"; staticconstchar * fatal_static_field_not_found = "Static field not found in JNI get/set field operations"; staticconstchar * fatal_static_field_mismatch = "Field type (static) mismatch in JNI get/set field operations"; staticconstchar * fatal_should_be_nonstatic = "Static field ID passed to JNI"; staticconstchar * fatal_null_object = "Null object passed to JNI"; staticconstchar * fatal_wrong_field = "Wrong field ID passed to JNI"; staticconstchar * fatal_instance_field_not_found = "Instance field not found in JNI get/set field operations"; staticconstchar * fatal_instance_field_mismatch = "Field type (instance) mismatch in JNI get/set field operations"; staticconstchar * fatal_non_string = "JNI string operation received a non-string"; staticconstchar * fatal_non_utf8_class_name1 = "JNI class name is not a valid UTF8 string \""; staticconstchar * fatal_non_utf8_class_name2 = "\"";
// When in VM state: staticvoid ReportJNIWarning(JavaThread* thr, constchar *msg) {
tty->print_cr("WARNING in native method: %s", msg);
thr->print_stack();
}
// When in NATIVE state: staticvoid NativeReportJNIFatalError(JavaThread* thr, constchar *msg) {
IN_VM(
ReportJNIFatalError(thr, msg);
)
}
/** * Check whether or not a programmer has actually checked for exceptions. According * to the JNI Specification ("jni/spec/design.html#java_exceptions"): * * There are two cases where the programmer needs to check for exceptions without * being able to first check an error code: * * - The JNI functions that invoke a Java method return the result of the Java method. * The programmer must call ExceptionOccurred() to check for possible exceptions * that occurred during the execution of the Java method. * * - Some of the JNI array access functions do not return an error code, but may * throw an ArrayIndexOutOfBoundsException or ArrayStoreException. * * In all other cases, a non-error return value guarantees that no exceptions have been thrown. * * Programmers often defend against ArrayIndexOutOfBoundsException, so warning * for these functions would be pedantic.
*/ staticinlinevoid
check_pending_exception(JavaThread* thr) { if (thr->has_pending_exception()) {
NativeReportJNIWarning(thr, "JNI call made with exception pending");
} if (thr->is_pending_jni_exception_check()) {
IN_VM(
tty->print_cr("WARNING in native method: JNI call made without checking exceptions when required to from %s",
thr->get_pending_jni_exception_check());
thr->print_stack();
)
thr->clear_pending_jni_exception_check(); // Just complain once
}
}
/* make sure it is an instance field */ if (jfieldIDWorkaround::is_static_jfieldID(fid))
ReportJNIFatalError(thr, fatal_should_be_nonstatic);
/* validate the object being passed and then get its class */
ASSERT_OOPS_ALLOWED;
oop oopObj = jniCheck::validate_object(thr, obj); if (oopObj == NULL) {
ReportJNIFatalError(thr, fatal_null_object);
}
Klass* k_oop = oopObj->klass();
if (!jfieldIDWorkaround::is_valid_jfieldID(k_oop, fid)) {
ReportJNIFatalError(thr, fatal_wrong_field);
}
/* make sure the field exists */ int offset = jfieldIDWorkaround::from_instance_jfieldID(k_oop, fid); if (!InstanceKlass::cast(k_oop)->contains_field_offset(offset))
ReportJNIFatalError(thr, fatal_wrong_field);
/* check for proper field type */ if (!InstanceKlass::cast(k_oop)->find_field_from_offset(offset, false, &fd))
ReportJNIFatalError(thr, fatal_instance_field_not_found);
Method* jniCheck::validate_jmethod_id(JavaThread* thr, jmethodID method_id) {
ASSERT_OOPS_ALLOWED; // do the fast jmethodID check first
Method* m = Method::checked_resolve_jmethod_id(method_id); if (m == NULL) {
ReportJNIFatalError(thr, fatal_wrong_class_or_method);
} // jmethodIDs are handles in the class loader data, // but that can be expensive so check it last elseif (!Method::is_method_id(method_id)) {
ReportJNIFatalError(thr, fatal_non_weak_method);
} return m;
}
// Warn if a class descriptor is in decorated form; class descriptors // passed to JNI findClass should not be decorated unless they are // array descriptors. void jniCheck::validate_class_descriptor(JavaThread* thr, constchar* name) { if (name == NULL) return; // implementation accepts NULL so just return
// Verify that the class name given is a valid utf8 string if (!UTF8::is_legal_utf8((constunsignedchar*)name, (int)strlen(name), false)) { char msg[JVM_MAXPATHLEN];
jio_snprintf(msg, JVM_MAXPATHLEN, "%s%s%s", fatal_non_utf8_class_name1, name, fatal_non_utf8_class_name2);
ReportJNIFatalError(thr, msg);
}
}
if (clazz != NULL) {
Klass* k = jniCheck::validate_class(thr, clazz, false); // Check that method is in the class, must be InstanceKlass if (!InstanceKlass::cast(k)->is_subtype_of(holder)) {
ReportJNIFatalError(thr, fatal_wrong_class_or_method);
}
}
// Check that the object is a subtype of method holder too. if (!rk->is_subtype_of(holder)) {
ReportJNIFatalError(thr, fatal_wrong_class_or_method);
}
}
}
/* * IMPLEMENTATION OF FUNCTIONS IN CHECKED TABLE
*/
JNI_ENTRY_CHECKED(void,
checked_jni_DeleteGlobalRef(JNIEnv *env,
jobject gref))
functionEnterExceptionAllowed(thr);
IN_VM(
jniCheck::validate_object(thr, gref); if (gref && !JNIHandles::is_global_handle(gref)) {
ReportJNIFatalError(thr, "Invalid global JNI handle passed to DeleteGlobalRef");
}
)
UNCHECKED()->DeleteGlobalRef(env,gref);
functionExit(thr);
JNI_END
JNI_ENTRY_CHECKED(void,
checked_jni_DeleteLocalRef(JNIEnv *env,
jobject obj))
functionEnterExceptionAllowed(thr);
IN_VM(
jniCheck::validate_object(thr, obj); if (obj && !(JNIHandles::is_local_handle(thr, obj) ||
JNIHandles::is_frame_handle(thr, obj)))
ReportJNIFatalError(thr, "Invalid local JNI handle passed to DeleteLocalRef");
)
UNCHECKED()->DeleteLocalRef(env, obj);
functionExit(thr);
JNI_END
JNI_ENTRY_CHECKED(jboolean,
checked_jni_IsSameObject(JNIEnv *env,
jobject obj1,
jobject obj2))
functionEnterExceptionAllowed(thr);
IN_VM( /* This JNI function can be used to compare weak global references * to NULL objects. If the handles are valid, but contain NULL, * then don't attempt to validate the object.
*/ if (obj1 != NULL && jniCheck::validate_handle(thr, obj1) != NULL) {
jniCheck::validate_object(thr, obj1);
} if (obj2 != NULL && jniCheck::validate_handle(thr, obj2) != NULL) {
jniCheck::validate_object(thr, obj2);
}
)
jboolean result = UNCHECKED()->IsSameObject(env,obj1,obj2);
functionExit(thr); return result;
JNI_END
// Arbitrary (but well-known) tag constvoid* STRING_TAG = (void*)0x47114711;
JNI_ENTRY_CHECKED(const jchar *,
checked_jni_GetStringChars(JNIEnv *env,
jstring str,
jboolean *isCopy))
functionEnter(thr);
IN_VM(
checkString(thr, str);
)
jchar* new_result = NULL; const jchar *result = UNCHECKED()->GetStringChars(env,str,isCopy);
assert (isCopy == NULL || *isCopy == JNI_TRUE, "GetStringChars didn't return a copy as expected"); if (result != NULL) {
size_t len = UNCHECKED()->GetStringLength(env,str) + 1; // + 1 for NULL termination
len *= sizeof(jchar);
new_result = (jchar*) GuardedMemory::wrap_copy(result, len, STRING_TAG); if (new_result == NULL) {
vm_exit_out_of_memory(len, OOM_MALLOC_ERROR, "checked_jni_GetStringChars");
} // Avoiding call to UNCHECKED()->ReleaseStringChars() since that will fire unexpected dtrace probes // Note that the dtrace arguments for the allocated memory will not match up with this solution.
FreeHeap((char*)result);
}
functionExit(thr); return new_result;
JNI_END
JNI_ENTRY_CHECKED(void,
checked_jni_ReleaseStringChars(JNIEnv *env,
jstring str, const jchar *chars))
functionEnterExceptionAllowed(thr);
IN_VM(
checkString(thr, str);
) if (chars == NULL) { // still do the unchecked call to allow dtrace probes
UNCHECKED()->ReleaseStringChars(env,str,chars);
} else {
GuardedMemory guarded((void*)chars); if (!guarded.verify_guards()) {
tty->print_cr("ReleaseStringChars: release chars failed bounds check. " "string: " PTR_FORMAT " chars: " PTR_FORMAT, p2i(str), p2i(chars));
guarded.print_on(tty);
NativeReportJNIFatalError(thr, "ReleaseStringChars: " "release chars failed bounds check.");
} if (guarded.get_tag() != STRING_TAG) {
tty->print_cr("ReleaseStringChars: called on something not allocated " "by GetStringChars. string: " PTR_FORMAT " chars: " PTR_FORMAT,
p2i(str), p2i(chars));
NativeReportJNIFatalError(thr, "ReleaseStringChars called on something " "not allocated by GetStringChars");
}
UNCHECKED()->ReleaseStringChars(env, str,
(const jchar*) guarded.release_for_freeing());
}
functionExit(thr);
JNI_END
// Arbitrary (but well-known) tag - different than GetStringChars constvoid* STRING_UTF_TAG = (void*) 0x48124812;
JNI_ENTRY_CHECKED(constchar *,
checked_jni_GetStringUTFChars(JNIEnv *env,
jstring str,
jboolean *isCopy))
functionEnter(thr);
IN_VM(
checkString(thr, str);
) char* new_result = NULL; constchar *result = UNCHECKED()->GetStringUTFChars(env,str,isCopy);
assert (isCopy == NULL || *isCopy == JNI_TRUE, "GetStringUTFChars didn't return a copy as expected"); if (result != NULL) {
size_t len = strlen(result) + 1; // + 1 for NULL termination
new_result = (char*) GuardedMemory::wrap_copy(result, len, STRING_UTF_TAG); if (new_result == NULL) {
vm_exit_out_of_memory(len, OOM_MALLOC_ERROR, "checked_jni_GetStringUTFChars");
} // Avoiding call to UNCHECKED()->ReleaseStringUTFChars() since that will fire unexpected dtrace probes // Note that the dtrace arguments for the allocated memory will not match up with this solution.
FreeHeap((char*)result);
}
functionExit(thr); return new_result;
JNI_END
JNI_ENTRY_CHECKED(void,
checked_jni_ReleaseStringUTFChars(JNIEnv *env,
jstring str, constchar* chars))
functionEnterExceptionAllowed(thr);
IN_VM(
checkString(thr, str);
) if (chars == NULL) { // still do the unchecked call to allow dtrace probes
UNCHECKED()->ReleaseStringUTFChars(env,str,chars);
} else {
GuardedMemory guarded((void*)chars); if (!guarded.verify_guards()) {
tty->print_cr("ReleaseStringUTFChars: release chars failed bounds check. " "string: " PTR_FORMAT " chars: " PTR_FORMAT, p2i(str), p2i(chars));
guarded.print_on(tty);
NativeReportJNIFatalError(thr, "ReleaseStringUTFChars: " "release chars failed bounds check.");
} if (guarded.get_tag() != STRING_UTF_TAG) {
tty->print_cr("ReleaseStringUTFChars: called on something not " "allocated by GetStringUTFChars. string: " PTR_FORMAT " chars: "
PTR_FORMAT, p2i(str), p2i(chars));
NativeReportJNIFatalError(thr, "ReleaseStringUTFChars " "called on something not allocated by GetStringUTFChars");
}
UNCHECKED()->ReleaseStringUTFChars(env, str,
(constchar*) guarded.release_for_freeing());
}
functionExit(thr);
JNI_END
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.