src/share/vm/prims/jniCheck.cpp

Print this page
rev 6504 : 6311046: -Xcheck:jni should support checking of GetPrimitiveArrayCritical.
Summary: Wrapped memory with standard bounds checking "GuardedMemory".
Reviewed-by: zgu, fparain, dcubed
rev 6505 : 8046662: Check JNI ReleaseStringChars / ReleaseStringUTFChars verify_guards test inverted
Summary: negative verify_guards when testing for corruption
Reviewed-by: sla, fparain

*** 23,32 **** --- 23,33 ---- */ #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" + #include "memory/guardedMemory.hpp" #include "oops/instanceKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" #include "prims/jni.h" #include "prims/jniCheck.hpp"
*** 321,330 **** --- 322,399 ---- if (!aOop->is_objArray()) { ReportJNIFatalError(thr, fatal_object_array_expected); } } + /* + * Copy and wrap array elements for bounds checking. + * Remember the original elements (GuardedMemory::get_tag()) + */ + static void* check_jni_wrap_copy_array(JavaThread* thr, jarray array, + void* orig_elements) { + void* result; + IN_VM( + oop a = JNIHandles::resolve_non_null(array); + size_t len = arrayOop(a)->length() << + TypeArrayKlass::cast(a->klass())->log2_element_size(); + result = GuardedMemory::wrap_copy(orig_elements, len, orig_elements); + ) + return result; + } + + static void* check_wrapped_array(JavaThread* thr, const char* fn_name, + void* obj, void* carray, size_t* rsz) { + if (carray == NULL) { + tty->print_cr("%s: elements vector NULL" PTR_FORMAT, fn_name, p2i(obj)); + NativeReportJNIFatalError(thr, "Elements vector NULL"); + } + GuardedMemory guarded(carray); + void* orig_result = guarded.get_tag(); + if (!guarded.verify_guards()) { + tty->print_cr("ReleasePrimitiveArrayCritical: release array failed bounds " + "check, incorrect pointer returned ? array: " PTR_FORMAT " carray: " + PTR_FORMAT, p2i(obj), p2i(carray)); + guarded.print_on(tty); + NativeReportJNIFatalError(thr, "ReleasePrimitiveArrayCritical: " + "failed bounds check"); + } + if (orig_result == NULL) { + tty->print_cr("ReleasePrimitiveArrayCritical: unrecognized elements. array: " + PTR_FORMAT " carray: " PTR_FORMAT, p2i(obj), p2i(carray)); + guarded.print_on(tty); + NativeReportJNIFatalError(thr, "ReleasePrimitiveArrayCritical: " + "unrecognized elements"); + } + if (rsz != NULL) { + *rsz = guarded.get_user_size(); + } + return orig_result; + } + + static void* check_wrapped_array_release(JavaThread* thr, const char* fn_name, + void* obj, void* carray, jint mode) { + size_t sz; + void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz); + switch (mode) { + case 0: + memcpy(orig_result, carray, sz); + GuardedMemory::free_copy(carray); + break; + case JNI_COMMIT: + memcpy(orig_result, carray, sz); + break; + case JNI_ABORT: + GuardedMemory::free_copy(carray); + break; + default: + tty->print_cr("%s: Unrecognized mode %i releasing array " + PTR_FORMAT " elements " PTR_FORMAT, fn_name, mode, p2i(obj), p2i(carray)); + NativeReportJNIFatalError(thr, "Unrecognized array release mode"); + } + return orig_result; + } + oop jniCheck::validate_handle(JavaThread* thr, jobject obj) { if (JNIHandles::is_frame_handle(thr, obj) || JNIHandles::is_local_handle(thr, obj) || JNIHandles::is_global_handle(obj) || JNIHandles::is_weak_global_handle(obj)) {
*** 1312,1346 **** functionExit(env); return result; JNI_END // Arbitrary (but well-known) tag ! const jint STRING_TAG = 0x47114711; JNI_ENTRY_CHECKED(const jchar *, checked_jni_GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy)) functionEnter(thr); IN_VM( checkString(thr, str); ) ! jchar* newResult = 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 ! jint* tagLocation = (jint*) AllocateHeap(len * sizeof(jchar) + sizeof(jint), mtInternal); ! *tagLocation = STRING_TAG; ! newResult = (jchar*) (tagLocation + 1); ! memcpy(newResult, result, len * sizeof(jchar)); // 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(env); ! return newResult; JNI_END JNI_ENTRY_CHECKED(void, checked_jni_ReleaseStringChars(JNIEnv *env, jstring str, --- 1381,1416 ---- functionExit(env); return result; JNI_END // Arbitrary (but well-known) tag ! const void* 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(env); ! return new_result; JNI_END JNI_ENTRY_CHECKED(void, checked_jni_ReleaseStringChars(JNIEnv *env, jstring str,
*** 1352,1366 **** if (chars == NULL) { // still do the unchecked call to allow dtrace probes UNCHECKED()->ReleaseStringChars(env,str,chars); } else { ! jint* tagLocation = ((jint*) chars) - 1; ! if (*tagLocation != STRING_TAG) { ! NativeReportJNIFatalError(thr, "ReleaseStringChars called on something not allocated by GetStringChars"); } ! UNCHECKED()->ReleaseStringChars(env,str,(const jchar*)tagLocation); } functionExit(env); JNI_END JNI_ENTRY_CHECKED(jstring, --- 1422,1448 ---- 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(env); JNI_END JNI_ENTRY_CHECKED(jstring,
*** 1383,1417 **** functionExit(env); return result; JNI_END // Arbitrary (but well-known) tag - different than GetStringChars ! const jint STRING_UTF_TAG = 0x48124812; JNI_ENTRY_CHECKED(const char *, checked_jni_GetStringUTFChars(JNIEnv *env, jstring str, jboolean *isCopy)) functionEnter(thr); IN_VM( checkString(thr, str); ) ! char* newResult = NULL; const char *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 ! jint* tagLocation = (jint*) AllocateHeap(len + sizeof(jint), mtInternal); ! *tagLocation = STRING_UTF_TAG; ! newResult = (char*) (tagLocation + 1); ! strcpy(newResult, result); // 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, mtInternal); } functionExit(env); ! return newResult; JNI_END JNI_ENTRY_CHECKED(void, checked_jni_ReleaseStringUTFChars(JNIEnv *env, jstring str, --- 1465,1499 ---- functionExit(env); return result; JNI_END // Arbitrary (but well-known) tag - different than GetStringChars ! const void* STRING_UTF_TAG = (void*) 0x48124812; JNI_ENTRY_CHECKED(const char *, checked_jni_GetStringUTFChars(JNIEnv *env, jstring str, jboolean *isCopy)) functionEnter(thr); IN_VM( checkString(thr, str); ) ! char* new_result = NULL; const char *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, mtInternal); } functionExit(env); ! return new_result; JNI_END JNI_ENTRY_CHECKED(void, checked_jni_ReleaseStringUTFChars(JNIEnv *env, jstring str,
*** 1423,1437 **** if (chars == NULL) { // still do the unchecked call to allow dtrace probes UNCHECKED()->ReleaseStringUTFChars(env,str,chars); } else { ! jint* tagLocation = ((jint*) chars) - 1; ! if (*tagLocation != STRING_UTF_TAG) { ! NativeReportJNIFatalError(thr, "ReleaseStringUTFChars called on something not allocated by GetStringUTFChars"); } ! UNCHECKED()->ReleaseStringUTFChars(env,str,(const char*)tagLocation); } functionExit(env); JNI_END JNI_ENTRY_CHECKED(jsize, --- 1505,1531 ---- 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, ! (const char*) guarded.release_for_freeing()); } functionExit(env); JNI_END JNI_ENTRY_CHECKED(jsize,
*** 1512,1521 **** --- 1606,1618 ---- check_primitive_array_type(thr, array, ElementTag); \ ) \ ElementType *result = UNCHECKED()->Get##Result##ArrayElements(env, \ array, \ isCopy); \ + if (result != NULL) { \ + result = (ElementType *) check_jni_wrap_copy_array(thr, array, result); \ + } \ functionExit(env); \ return result; \ JNI_END WRAPPER_GetScalarArrayElements(T_BOOLEAN, jboolean, Boolean)
*** 1536,1551 **** functionEnterExceptionAllowed(thr); \ IN_VM( \ check_primitive_array_type(thr, array, ElementTag); \ ASSERT_OOPS_ALLOWED; \ typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ - /* cannot check validity of copy, unless every request is logged by - * checking code. Implementation of this check is deferred until a - * subsequent release. - */ \ ) \ ! UNCHECKED()->Release##Result##ArrayElements(env,array,elems,mode); \ functionExit(env); \ JNI_END WRAPPER_ReleaseScalarArrayElements(T_BOOLEAN,jboolean, Boolean, bool) WRAPPER_ReleaseScalarArrayElements(T_BYTE, jbyte, Byte, byte) --- 1633,1646 ---- functionEnterExceptionAllowed(thr); \ IN_VM( \ check_primitive_array_type(thr, array, ElementTag); \ ASSERT_OOPS_ALLOWED; \ typeArrayOop a = typeArrayOop(JNIHandles::resolve_non_null(array)); \ ) \ ! ElementType* orig_result = (ElementType *) check_wrapped_array_release( \ ! thr, "checked_jni_Release"#Result"ArrayElements", array, elems, mode); \ ! UNCHECKED()->Release##Result##ArrayElements(env, array, orig_result, mode); \ functionExit(env); \ JNI_END WRAPPER_ReleaseScalarArrayElements(T_BOOLEAN,jboolean, Boolean, bool) WRAPPER_ReleaseScalarArrayElements(T_BYTE, jbyte, Byte, byte)
*** 1692,1701 **** --- 1787,1799 ---- functionEnterCritical(thr); IN_VM( check_is_primitive_array(thr, array); ) void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy); + if (result != NULL) { + result = check_jni_wrap_copy_array(thr, array, result); + } functionExit(env); return result; JNI_END JNI_ENTRY_CHECKED(void,
*** 1705,1718 **** jint mode)) functionEnterCriticalExceptionAllowed(thr); IN_VM( check_is_primitive_array(thr, array); ) ! /* The Hotspot JNI code does not use the parameters, so just check the ! * array parameter as a minor sanity check ! */ ! UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, carray, mode); functionExit(env); JNI_END JNI_ENTRY_CHECKED(const jchar*, checked_jni_GetStringCritical(JNIEnv *env, --- 1803,1815 ---- jint mode)) functionEnterCriticalExceptionAllowed(thr); IN_VM( check_is_primitive_array(thr, array); ) ! // Check the element array... ! void* orig_result = check_wrapped_array_release(thr, "ReleasePrimitiveArrayCritical", array, carray, mode); ! UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, orig_result, mode); functionExit(env); JNI_END JNI_ENTRY_CHECKED(const jchar*, checked_jni_GetStringCritical(JNIEnv *env,