提交 3925cfff 编写于 作者: D dsimms

6311046: -Xcheck:jni should support checking of GetPrimitiveArrayCritical.

Summary: Wrapped memory with standard bounds checking "GuardedMemory".
Reviewed-by: zgu, fparain, dcubed
上级 676405f8
/*
* Copyright (c) 2014, 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.
*
*/
#include "precompiled.hpp"
#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/guardedMemory.hpp"
#include "runtime/os.hpp"
void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) {
size_t total_sz = GuardedMemory::get_total_size(len);
void* outerp = os::malloc(total_sz, mtInternal);
if (outerp != NULL) {
GuardedMemory guarded(outerp, len, tag);
void* innerp = guarded.get_user_ptr();
memcpy(innerp, ptr, len);
return innerp;
}
return NULL; // OOM
}
bool GuardedMemory::free_copy(void* p) {
if (p == NULL) {
return true;
}
GuardedMemory guarded((u_char*)p);
bool verify_ok = guarded.verify_guards();
/* always attempt to free, pass problem on to any nested memchecker */
os::free(guarded.release_for_freeing());
return verify_ok;
}
void GuardedMemory::print_on(outputStream* st) const {
if (_base_addr == NULL) {
st->print_cr("GuardedMemory(" PTR_FORMAT ") not associated to any memory", p2i(this));
return;
}
st->print_cr("GuardedMemory(" PTR_FORMAT ") base_addr=" PTR_FORMAT
" tag=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT,
p2i(this), p2i(_base_addr), p2i(get_tag()), get_user_size(), p2i(get_user_ptr()));
Guard* guard = get_head_guard();
st->print_cr(" Header guard @" PTR_FORMAT " is %s", p2i(guard), (guard->verify() ? "OK" : "BROKEN"));
guard = get_tail_guard();
st->print_cr(" Trailer guard @" PTR_FORMAT " is %s", p2i(guard), (guard->verify() ? "OK" : "BROKEN"));
u_char udata = *get_user_ptr();
switch (udata) {
case uninitBlockPad:
st->print_cr(" User data appears unused");
break;
case freeBlockPad:
st->print_cr(" User data appears to have been freed");
break;
default:
st->print_cr(" User data appears to be in use");
break;
}
}
// test code...
#ifndef PRODUCT
static void guarded_memory_test_check(void* p, size_t sz, void* tag) {
assert(p != NULL, "NULL pointer given to check");
u_char* c = (u_char*) p;
GuardedMemory guarded(c);
assert(guarded.get_tag() == tag, "Tag is not the same as supplied");
assert(guarded.get_user_ptr() == c, "User pointer is not the same as supplied");
assert(guarded.get_user_size() == sz, "User size is not the same as supplied");
assert(guarded.verify_guards(), "Guard broken");
}
void GuardedMemory::test_guarded_memory() {
// Test the basic characteristics...
size_t total_sz = GuardedMemory::get_total_size(1);
assert(total_sz > 1 && total_sz >= (sizeof(GuardHeader) + 1 + sizeof(Guard)), "Unexpected size");
u_char* basep = (u_char*) os::malloc(total_sz, mtInternal);
GuardedMemory guarded(basep, 1, (void*)0xf000f000);
assert(*basep == badResourceValue, "Expected guard in the form of badResourceValue");
u_char* userp = guarded.get_user_ptr();
assert(*userp == uninitBlockPad, "Expected uninitialized data in the form of uninitBlockPad");
guarded_memory_test_check(userp, 1, (void*)0xf000f000);
void* freep = guarded.release_for_freeing();
assert((u_char*)freep == basep, "Expected the same pointer guard was ");
assert(*userp == freeBlockPad, "Expected user data to be free block padded");
assert(!guarded.verify_guards(), "Expected failed");
os::free(freep);
// Test a number of odd sizes...
size_t sz = 0;
do {
void* p = os::malloc(GuardedMemory::get_total_size(sz), mtInternal);
void* up = guarded.wrap_with_guards(p, sz, (void*)1);
memset(up, 0, sz);
guarded_memory_test_check(up, sz, (void*)1);
os::free(guarded.release_for_freeing());
sz = (sz << 4) + 1;
} while (sz < (256 * 1024));
// Test buffer overrun into head...
basep = (u_char*) os::malloc(GuardedMemory::get_total_size(1), mtInternal);
guarded.wrap_with_guards(basep, 1);
*basep = 0;
assert(!guarded.verify_guards(), "Expected failure");
os::free(basep);
// Test buffer overrun into tail with a number of odd sizes...
sz = 1;
do {
void* p = os::malloc(GuardedMemory::get_total_size(sz), mtInternal);
void* up = guarded.wrap_with_guards(p, sz, (void*)1);
memset(up, 0, sz + 1); // Buffer-overwrite (within guard)
assert(!guarded.verify_guards(), "Guard was not broken as expected");
os::free(guarded.release_for_freeing());
sz = (sz << 4) + 1;
} while (sz < (256 * 1024));
// Test wrap_copy/wrap_free...
assert(GuardedMemory::free_copy(NULL), "Expected free NULL to be OK");
const char* str = "Check my bounds out";
size_t str_sz = strlen(str) + 1;
char* str_copy = (char*) GuardedMemory::wrap_copy(str, str_sz);
guarded_memory_test_check(str_copy, str_sz, NULL);
assert(strcmp(str, str_copy) == 0, "Not identical copy");
assert(GuardedMemory::free_copy(str_copy), "Free copy failed to verify");
void* no_data = NULL;
void* no_data_copy = GuardedMemory::wrap_copy(no_data, 0);
assert(GuardedMemory::free_copy(no_data_copy), "Expected valid guards even for no data copy");
}
#endif // !PRODUCT
/*
* Copyright (c) 2014, 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.
*
*/
#ifndef SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
#define SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
/**
* Guarded memory for detecting buffer overrun.
*
* Allows allocations to be wrapped with padded bytes of a known byte pattern,
* that is a "guard". Guard patterns may be verified to detect buffer overruns.
*
* Primarily used by "debug malloc" and "checked JNI".
*
* Memory layout:
*
* |Offset | Content | Description |
* |------------------------------------------------------------
* |base_addr | 0xABABABABABABABAB | Head guard |
* |+16 | <size_t:user_size> | User data size |
* |+sizeof(uintptr_t) | <tag> | Tag word |
* |+sizeof(void*) | 0xF1 <user_data> ( | User data |
* |+user_size | 0xABABABABABABABAB | Tail guard |
* -------------------------------------------------------------
*
* Where:
* - guard padding uses "badResourceValue" (0xAB)
* - tag word is general purpose
* - user data
* -- initially padded with "uninitBlockPad" (0xF1),
* -- to "freeBlockPad" (0xBA), when freed
*
* Usage:
*
* * Allocations: one may wrap allocations with guard memory:
* <code>
* Thing* alloc_thing() {
* void* mem = user_alloc_fn(GuardedMemory::get_total_size(sizeof(thing)));
* GuardedMemory guarded(mem, sizeof(thing));
* return (Thing*) guarded.get_user_ptr();
* }
* </code>
* * Verify: memory guards are still in tact
* <code>
* bool verify_thing(Thing* thing) {
* GuardedMemory guarded((void*)thing);
* return guarded.verify_guards();
* }
* </code>
* * Free: one may mark bytes as freed (further debugging support)
* <code>
* void free_thing(Thing* thing) {
* GuardedMemory guarded((void*)thing);
* assert(guarded.verify_guards(), "Corrupt thing");
* user_free_fn(guards.release_for_freeing();
* }
* </code>
*/
class GuardedMemory : StackObj { // Wrapper on stack
// Private inner classes for memory layout...
protected:
/**
* Guard class for header and trailer known pattern to test for overwrites.
*/
class Guard { // Class for raw memory (no vtbl allowed)
friend class GuardedMemory;
protected:
enum {
GUARD_SIZE = 16
};
u_char _guard[GUARD_SIZE];
public:
void build() {
u_char* c = _guard; // Possibly unaligned if tail guard
u_char* end = c + GUARD_SIZE;
while (c < end) {
*c = badResourceValue;
c++;
}
}
bool verify() const {
u_char* c = (u_char*) _guard;
u_char* end = c + GUARD_SIZE;
while (c < end) {
if (*c != badResourceValue) {
return false;
}
c++;
}
return true;
}
}; // GuardedMemory::Guard
/**
* Header guard and size
*/
class GuardHeader : Guard {
friend class GuardedMemory;
protected:
// Take care in modifying fields here, will effect alignment
// e.g. x86 ABI 16 byte stack alignment
union {
uintptr_t __unused_full_word1;
size_t _user_size;
};
void* _tag;
public:
void set_user_size(const size_t usz) { _user_size = usz; }
size_t get_user_size() const { return _user_size; }
void set_tag(const void* tag) { _tag = (void*) tag; }
void* get_tag() const { return _tag; }
}; // GuardedMemory::GuardHeader
// Guarded Memory...
protected:
u_char* _base_addr;
public:
/**
* Create new guarded memory.
*
* Wraps, starting at the given "base_ptr" with guards. Use "get_user_ptr()"
* to return a pointer suitable for user data.
*
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
* @param user_size the size of the user data to be wrapped.
* @param tag optional general purpose tag.
*/
GuardedMemory(void* base_ptr, const size_t user_size, const void* tag = NULL) {
wrap_with_guards(base_ptr, user_size, tag);
}
/**
* Wrap existing guarded memory.
*
* To use this constructor, one must have created guarded memory with
* "GuardedMemory(void*, size_t, void*)" (or indirectly via helper, e.g. "wrap_copy()").
*
* @param user_p existing wrapped memory.
*/
GuardedMemory(void* userp) {
u_char* user_ptr = (u_char*) userp;
assert((uintptr_t)user_ptr > (sizeof(GuardHeader) + 0x1000), "Invalid pointer");
_base_addr = (user_ptr - sizeof(GuardHeader));
}
/**
* Create new guarded memory.
*
* Wraps, starting at the given "base_ptr" with guards. Allows reuse of stack allocated helper.
*
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
* @param user_size the size of the user data to be wrapped.
* @param tag optional general purpose tag.
*
* @return user data pointer (inner pointer to supplied "base_ptr").
*/
void* wrap_with_guards(void* base_ptr, size_t user_size, const void* tag = NULL) {
assert(base_ptr != NULL, "Attempt to wrap NULL with memory guard");
_base_addr = (u_char*)base_ptr;
get_head_guard()->build();
get_head_guard()->set_user_size(user_size);
get_tail_guard()->build();
set_tag(tag);
set_user_bytes(uninitBlockPad);
assert(verify_guards(), "Expected valid memory guards");
return get_user_ptr();
}
/**
* Verify head and tail guards.
*
* @return true if guards are intact, false would indicate a buffer overrun.
*/
bool verify_guards() const {
if (_base_addr != NULL) {
return (get_head_guard()->verify() && get_tail_guard()->verify());
}
return false;
}
/**
* Set the general purpose tag.
*
* @param tag general purpose tag.
*/
void set_tag(const void* tag) { get_head_guard()->set_tag(tag); }
/**
* Return the general purpose tag.
*
* @return the general purpose tag, defaults to NULL.
*/
void* get_tag() const { return get_head_guard()->get_tag(); }
/**
* Return the size of the user data.
*
* @return the size of the user data.
*/
size_t get_user_size() const {
assert(_base_addr, "Not wrapping any memory");
return get_head_guard()->get_user_size();
}
/**
* Return the user data pointer.
*
* @return the user data pointer.
*/
u_char* get_user_ptr() const {
assert(_base_addr, "Not wrapping any memory");
return _base_addr + sizeof(GuardHeader);
}
/**
* Release the wrapped pointer for resource freeing.
*
* Pads the user data with "freeBlockPad", and dis-associates the helper.
*
* @return the original base pointer used to wrap the data.
*/
void* release_for_freeing() {
set_user_bytes(freeBlockPad);
return release();
}
/**
* Dis-associate the help from the original base address.
*
* @return the original base pointer used to wrap the data.
*/
void* release() {
void* p = (void*) _base_addr;
_base_addr = NULL;
return p;
}
virtual void print_on(outputStream* st) const;
protected:
GuardHeader* get_head_guard() const { return (GuardHeader*) _base_addr; }
Guard* get_tail_guard() const { return (Guard*) (get_user_ptr() + get_user_size()); };
void set_user_bytes(u_char ch) {
memset(get_user_ptr(), ch, get_user_size());
}
public:
/**
* Return the total size required for wrapping the given user size.
*
* @return the total size required for wrapping the given user size.
*/
static size_t get_total_size(size_t user_size) {
size_t total_size = sizeof(GuardHeader) + user_size + sizeof(Guard);
assert(total_size > user_size, "Unexpected wrap-around");
return total_size;
}
// Helper functions...
/**
* Wrap a copy of size "len" of "ptr".
*
* @param ptr the memory to be copied
* @param len the length of the copy
* @param tag optional general purpose tag (see GuardedMemory::get_tag())
*
* @return guarded wrapped memory pointer to the user area, or NULL if OOM.
*/
static void* wrap_copy(const void* p, const size_t len, const void* tag = NULL);
/**
* Free wrapped copy.
*
* Frees memory copied with "wrap_copy()".
*
* @param p memory returned by "wrap_copy()".
*
* @return true if guards were verified as intact. false indicates a buffer overrun.
*/
static bool free_copy(void* p);
// Testing...
#ifndef PRODUCT
static void test_guarded_memory(void);
#endif
}; // GuardedMemory
#endif // SHARE_VM_MEMORY_GUARDED_MEMORY_HPP
......@@ -5064,6 +5064,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) {
#if INCLUDE_ALL_GCS
#include "gc_implementation/g1/heapRegionRemSet.hpp"
#endif
#include "memory/guardedMemory.hpp"
#include "utilities/quickSort.hpp"
#include "utilities/ostream.hpp"
#if INCLUDE_VM_STRUCTS
......@@ -5104,6 +5105,7 @@ void execute_internal_vm_tests() {
run_unit_test(arrayOopDesc::test_max_array_length());
run_unit_test(CollectedHeap::test_is_in());
run_unit_test(QuickSort::test_quick_sort());
run_unit_test(GuardedMemory::test_guarded_memory());
run_unit_test(AltHashing::test_alt_hash());
run_unit_test(test_loggc_filename());
run_unit_test(TestNewSize_test());
......
......@@ -25,6 +25,7 @@
#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"
......@@ -323,6 +324,74 @@ check_is_obj_array(JavaThread* thr, jarray jArray) {
}
}
/*
* 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) ||
......@@ -1314,7 +1383,7 @@ JNI_ENTRY_CHECKED(jsize,
JNI_END
// Arbitrary (but well-known) tag
const jint STRING_TAG = 0x47114711;
const void* STRING_TAG = (void*)0x47114711;
JNI_ENTRY_CHECKED(const jchar *,
checked_jni_GetStringChars(JNIEnv *env,
......@@ -1324,21 +1393,22 @@ JNI_ENTRY_CHECKED(const jchar *,
IN_VM(
checkString(thr, str);
)
jchar* newResult = NULL;
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
jint* tagLocation = (jint*) AllocateHeap(len * sizeof(jchar) + sizeof(jint), mtInternal);
*tagLocation = STRING_TAG;
newResult = (jchar*) (tagLocation + 1);
memcpy(newResult, result, len * sizeof(jchar));
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 newResult;
return new_result;
JNI_END
JNI_ENTRY_CHECKED(void,
......@@ -1354,11 +1424,23 @@ JNI_ENTRY_CHECKED(void,
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");
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*)tagLocation);
UNCHECKED()->ReleaseStringChars(env, str,
(const jchar*) guarded.release_for_freeing());
}
functionExit(env);
JNI_END
......@@ -1385,7 +1467,7 @@ JNI_ENTRY_CHECKED(jsize,
JNI_END
// Arbitrary (but well-known) tag - different than GetStringChars
const jint STRING_UTF_TAG = 0x48124812;
const void* STRING_UTF_TAG = (void*) 0x48124812;
JNI_ENTRY_CHECKED(const char *,
checked_jni_GetStringUTFChars(JNIEnv *env,
......@@ -1395,21 +1477,21 @@ JNI_ENTRY_CHECKED(const char *,
IN_VM(
checkString(thr, str);
)
char* newResult = NULL;
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
jint* tagLocation = (jint*) AllocateHeap(len + sizeof(jint), mtInternal);
*tagLocation = STRING_UTF_TAG;
newResult = (char*) (tagLocation + 1);
strcpy(newResult, result);
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 newResult;
return new_result;
JNI_END
JNI_ENTRY_CHECKED(void,
......@@ -1425,11 +1507,23 @@ JNI_ENTRY_CHECKED(void,
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");
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.");
}
UNCHECKED()->ReleaseStringUTFChars(env,str,(const char*)tagLocation);
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
......@@ -1514,6 +1608,9 @@ JNI_ENTRY_CHECKED(ElementType *, \
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
......@@ -1538,12 +1635,10 @@ JNI_ENTRY_CHECKED(void, \
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); \
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
......@@ -1694,6 +1789,9 @@ JNI_ENTRY_CHECKED(void *,
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
......@@ -1707,10 +1805,9 @@ JNI_ENTRY_CHECKED(void,
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);
// 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
......
......@@ -32,6 +32,9 @@
#include "gc_implementation/shared/vmGCOperations.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/allocation.inline.hpp"
#ifdef ASSERT
#include "memory/guardedMemory.hpp"
#endif
#include "oops/oop.inline.hpp"
#include "prims/jvm.h"
#include "prims/jvm_misc.hpp"
......@@ -524,118 +527,16 @@ char *os::strdup(const char *str, MEMFLAGS flags) {
#ifdef ASSERT
#define space_before (MallocCushion + sizeof(double))
#define space_after MallocCushion
#define size_addr_from_base(p) (size_t*)(p + space_before - sizeof(size_t))
#define size_addr_from_obj(p) ((size_t*)p - 1)
// MallocCushion: size of extra cushion allocated around objects with +UseMallocOnly
// NB: cannot be debug variable, because these aren't set from the command line until
// *after* the first few allocs already happened
#define MallocCushion 16
#else
#define space_before 0
#define space_after 0
#define size_addr_from_base(p) should not use w/o ASSERT
#define size_addr_from_obj(p) should not use w/o ASSERT
#define MallocCushion 0
#endif
#define paranoid 0 /* only set to 1 if you suspect checking code has bug */
#ifdef ASSERT
inline size_t get_size(void* obj) {
size_t size = *size_addr_from_obj(obj);
if (size < 0) {
fatal(err_msg("free: size field of object #" PTR_FORMAT " was overwritten ("
SIZE_FORMAT ")", obj, size));
}
return size;
}
u_char* find_cushion_backwards(u_char* start) {
u_char* p = start;
while (p[ 0] != badResourceValue || p[-1] != badResourceValue ||
p[-2] != badResourceValue || p[-3] != badResourceValue) p--;
// ok, we have four consecutive marker bytes; find start
u_char* q = p - 4;
while (*q == badResourceValue) q--;
return q + 1;
}
u_char* find_cushion_forwards(u_char* start) {
u_char* p = start;
while (p[0] != badResourceValue || p[1] != badResourceValue ||
p[2] != badResourceValue || p[3] != badResourceValue) p++;
// ok, we have four consecutive marker bytes; find end of cushion
u_char* q = p + 4;
while (*q == badResourceValue) q++;
return q - MallocCushion;
}
void print_neighbor_blocks(void* ptr) {
// find block allocated before ptr (not entirely crash-proof)
if (MallocCushion < 4) {
tty->print_cr("### cannot find previous block (MallocCushion < 4)");
return;
}
u_char* start_of_this_block = (u_char*)ptr - space_before;
u_char* end_of_prev_block_data = start_of_this_block - space_after -1;
// look for cushion in front of prev. block
u_char* start_of_prev_block = find_cushion_backwards(end_of_prev_block_data);
ptrdiff_t size = *size_addr_from_base(start_of_prev_block);
u_char* obj = start_of_prev_block + space_before;
if (size <= 0 ) {
// start is bad; mayhave been confused by OS data inbetween objects
// search one more backwards
start_of_prev_block = find_cushion_backwards(start_of_prev_block);
size = *size_addr_from_base(start_of_prev_block);
obj = start_of_prev_block + space_before;
}
if (start_of_prev_block + space_before + size + space_after == start_of_this_block) {
tty->print_cr("### previous object: " PTR_FORMAT " (" SSIZE_FORMAT " bytes)", obj, size);
} else {
tty->print_cr("### previous object (not sure if correct): " PTR_FORMAT " (" SSIZE_FORMAT " bytes)", obj, size);
}
// now find successor block
u_char* start_of_next_block = (u_char*)ptr + *size_addr_from_obj(ptr) + space_after;
start_of_next_block = find_cushion_forwards(start_of_next_block);
u_char* next_obj = start_of_next_block + space_before;
ptrdiff_t next_size = *size_addr_from_base(start_of_next_block);
if (start_of_next_block[0] == badResourceValue &&
start_of_next_block[1] == badResourceValue &&
start_of_next_block[2] == badResourceValue &&
start_of_next_block[3] == badResourceValue) {
tty->print_cr("### next object: " PTR_FORMAT " (" SSIZE_FORMAT " bytes)", next_obj, next_size);
} else {
tty->print_cr("### next object (not sure if correct): " PTR_FORMAT " (" SSIZE_FORMAT " bytes)", next_obj, next_size);
}
}
void report_heap_error(void* memblock, void* bad, const char* where) {
static void verify_memory(void* ptr) {
GuardedMemory guarded(ptr);
if (!guarded.verify_guards()) {
tty->print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees);
tty->print_cr("## memory stomp: byte at " PTR_FORMAT " %s object " PTR_FORMAT, bad, where, memblock);
print_neighbor_blocks(memblock);
tty->print_cr("## memory stomp:");
guarded.print_on(tty);
fatal("memory stomping error");
}
void verify_block(void* memblock) {
size_t size = get_size(memblock);
if (MallocCushion) {
u_char* ptr = (u_char*)memblock - space_before;
for (int i = 0; i < MallocCushion; i++) {
if (ptr[i] != badResourceValue) {
report_heap_error(memblock, ptr+i, "in front of");
}
}
u_char* end = (u_char*)memblock + size + space_after;
for (int j = -MallocCushion; j < 0; j++) {
if (end[j] != badResourceValue) {
report_heap_error(memblock, end+j, "after");
}
}
}
}
#endif
......@@ -686,16 +587,18 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) {
size = 1;
}
const size_t alloc_size = size + space_before + space_after;
#ifndef ASSERT
const size_t alloc_size = size;
#else
const size_t alloc_size = GuardedMemory::get_total_size(size);
if (size > alloc_size) { // Check for rollover.
return NULL;
}
#endif
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
u_char* ptr;
if (MallocMaxTestWords > 0) {
ptr = testMalloc(alloc_size);
} else {
......@@ -703,28 +606,26 @@ void* os::malloc(size_t size, MEMFLAGS memflags, address caller) {
}
#ifdef ASSERT
if (ptr == NULL) return NULL;
if (MallocCushion) {
for (u_char* p = ptr; p < ptr + MallocCushion; p++) *p = (u_char)badResourceValue;
u_char* end = ptr + space_before + size;
for (u_char* pq = ptr+MallocCushion; pq < end; pq++) *pq = (u_char)uninitBlockPad;
for (u_char* q = end; q < end + MallocCushion; q++) *q = (u_char)badResourceValue;
}
// put size just before data
*size_addr_from_base(ptr) = size;
if (ptr == NULL) {
return NULL;
}
// Wrap memory with guard
GuardedMemory guarded(ptr, size);
ptr = guarded.get_user_ptr();
#endif
u_char* memblock = ptr + space_before;
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, memblock);
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, ptr);
breakpoint();
}
debug_only(if (paranoid) verify_block(memblock));
if (PrintMalloc && tty != NULL) tty->print_cr("os::malloc " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, memblock);
debug_only(if (paranoid) verify_memory(ptr));
if (PrintMalloc && tty != NULL) {
tty->print_cr("os::malloc " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, ptr);
}
// we do not track MallocCushion memory
MemTracker::record_malloc((address)memblock, size, memflags, caller == 0 ? CALLER_PC : caller);
// we do not track guard memory
MemTracker::record_malloc((address)ptr, size, memflags, caller == 0 ? CALLER_PC : caller);
return memblock;
return ptr;
}
......@@ -743,27 +644,32 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, address caller
return ptr;
#else
if (memblock == NULL) {
return malloc(size, memflags, (caller == 0 ? CALLER_PC : caller));
return os::malloc(size, memflags, (caller == 0 ? CALLER_PC : caller));
}
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::realloc caught " PTR_FORMAT, memblock);
breakpoint();
}
verify_block(memblock);
verify_memory(memblock);
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
if (size == 0) return NULL;
if (size == 0) {
return NULL;
}
// always move the block
void* ptr = malloc(size, memflags, caller == 0 ? CALLER_PC : caller);
if (PrintMalloc) tty->print_cr("os::remalloc " SIZE_FORMAT " bytes, " PTR_FORMAT " --> " PTR_FORMAT, size, memblock, ptr);
void* ptr = os::malloc(size, memflags, caller == 0 ? CALLER_PC : caller);
if (PrintMalloc) {
tty->print_cr("os::remalloc " SIZE_FORMAT " bytes, " PTR_FORMAT " --> " PTR_FORMAT, size, memblock, ptr);
}
// Copy to new memory if malloc didn't fail
if ( ptr != NULL ) {
memcpy(ptr, memblock, MIN2(size, get_size(memblock)));
if (paranoid) verify_block(ptr);
GuardedMemory guarded(memblock);
memcpy(ptr, memblock, MIN2(size, guarded.get_user_size()));
if (paranoid) verify_memory(ptr);
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
tty->print_cr("os::realloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, ptr);
breakpoint();
}
free(memblock);
os::free(memblock);
}
return ptr;
#endif
......@@ -771,6 +677,7 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, address caller
void os::free(void *memblock, MEMFLAGS memflags) {
address trackp = (address) memblock;
NOT_PRODUCT(inc_stat_counter(&num_frees, 1));
#ifdef ASSERT
if (memblock == NULL) return;
......@@ -778,34 +685,20 @@ void os::free(void *memblock, MEMFLAGS memflags) {
if (tty != NULL) tty->print_cr("os::free caught " PTR_FORMAT, memblock);
breakpoint();
}
verify_block(memblock);
verify_memory(memblock);
NOT_PRODUCT(if (MallocVerifyInterval > 0) check_heap());
// Added by detlefs.
if (MallocCushion) {
u_char* ptr = (u_char*)memblock - space_before;
for (u_char* p = ptr; p < ptr + MallocCushion; p++) {
guarantee(*p == badResourceValue,
"Thing freed should be malloc result.");
*p = (u_char)freeBlockPad;
}
size_t size = get_size(memblock);
GuardedMemory guarded(memblock);
size_t size = guarded.get_user_size();
inc_stat_counter(&free_bytes, size);
u_char* end = ptr + space_before + size;
for (u_char* q = end; q < end + MallocCushion; q++) {
guarantee(*q == badResourceValue,
"Thing freed should be malloc result.");
*q = (u_char)freeBlockPad;
}
if (PrintMalloc && tty != NULL)
memblock = guarded.release_for_freeing();
if (PrintMalloc && tty != NULL) {
fprintf(stderr, "os::free " SIZE_FORMAT " bytes --> " PTR_FORMAT "\n", size, (uintptr_t)memblock);
} else if (PrintMalloc && tty != NULL) {
// tty->print_cr("os::free %p", memblock);
fprintf(stderr, "os::free " PTR_FORMAT "\n", (uintptr_t)memblock);
}
#endif
MemTracker::record_free((address)memblock, memflags);
MemTracker::record_free(trackp, memflags);
::free((char*)memblock - space_before);
::free(memblock);
}
void os::init_random(long initval) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册