--- old/src/share/vm/prims/jni.cpp 2014-05-06 10:39:52.600291210 +0200 +++ new/src/share/vm/prims/jni.cpp 2014-05-06 10:39:52.472291205 +0200 @@ -3857,6 +3857,7 @@ #if INCLUDE_ALL_GCS #include "gc_implementation/g1/heapRegionRemSet.hpp" #endif +#include "memory/fencedMemory.hpp" #include "utilities/quickSort.hpp" #include "utilities/ostream.hpp" #if INCLUDE_VM_STRUCTS @@ -3900,6 +3901,7 @@ 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(FencedMemory::test_fenced_memory()); run_unit_test(AltHashing::test_alt_hash()); run_unit_test(test_loggc_filename()); run_unit_test(TestNewSize_test()); --- old/src/share/vm/prims/jniCheck.cpp 2014-05-06 10:39:53.084291233 +0200 +++ new/src/share/vm/prims/jniCheck.cpp 2014-05-06 10:39:52.956291227 +0200 @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" +#include "memory/fencedMemory.hpp" #include "oops/instanceKlass.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" @@ -323,6 +324,61 @@ } } +/* + * Copy and wrap array elements for bounds checking. + * Remember the original elements (FencedMemory::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 = FencedMemory::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, obj); + NativeReportJNIFatalError(thr, "Elements vector NULL"); + } + FencedMemory fenced(carray); + void* orig_result = fenced.get_tag(); + if ((!fenced.verify_fences()) || (orig_result == NULL)) { + tty->print_cr("ReleasePrimitiveArrayCritical: unrecognized elements" + PTR_FORMAT " elements " PTR_FORMAT, obj, carray); + fenced.print_on(tty); + NativeReportJNIFatalError(thr, "Unrecognized elements"); + } + if (rsz != NULL) { + *rsz = fenced.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); + FencedMemory::free_copy(carray); + break; + case JNI_COMMIT: + memcpy(orig_result, carray, sz); + break; + case JNI_ABORT: + FencedMemory::free_copy(carray); + break; + default: + tty->print_cr("%s: Unrecognized mode %i releasing array " + PTR_FORMAT " elements " PTR_FORMAT, fn_name, mode, obj, 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 +1370,7 @@ 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 +1380,22 @@ 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*) FencedMemory::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 +1411,11 @@ 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"); + FencedMemory fenced((void*)chars); + if ((!fenced.verify_fences()) || (fenced.get_tag() != STRING_TAG)) { + NativeReportJNIFatalError(thr, "ReleaseStringChars called on something not allocated by GetStringChars"); } - UNCHECKED()->ReleaseStringChars(env,str,(const jchar*)tagLocation); + UNCHECKED()->ReleaseStringChars(env, str, (const jchar*) fenced.release_for_freeing()); } functionExit(env); JNI_END @@ -1385,7 +1442,7 @@ 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 +1452,21 @@ 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*) FencedMemory::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 +1482,11 @@ 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); + FencedMemory fenced((void*)chars); + if ((!fenced.verify_fences()) || (fenced.get_tag() != STRING_UTF_TAG)) { + NativeReportJNIFatalError(thr, "ReleaseStringUTFChars called on something not allocated by GetStringUTFChars"); + } + UNCHECKED()->ReleaseStringUTFChars(env,str,(const char*) fenced.release_for_freeing()); } functionExit(env); JNI_END @@ -1514,6 +1571,9 @@ 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 +1598,10 @@ 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 +1752,9 @@ 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 +1768,9 @@ 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 --- old/src/share/vm/runtime/os.cpp 2014-05-06 10:39:53.552291255 +0200 +++ new/src/share/vm/runtime/os.cpp 2014-05-06 10:39:53.424291249 +0200 @@ -521,121 +521,21 @@ } - -#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; -} +#include "memory/fencedMemory.hpp" -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; may have been confused by OS data in between 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); +static void verify_memory(void* ptr) { + FencedMemory fenced(ptr); + if (!fenced.verify_fences()) { + tty->print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees); + tty->print_cr("## memory stomp:"); + fenced.print_on(tty); + fatal("memory stomping error"); } } - -void report_heap_error(void* memblock, void* bad, const char* where) { - 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); - 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 // @@ -684,16 +584,18 @@ 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 = FencedMemory::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 { @@ -701,28 +603,26 @@ } #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; + if (ptr == NULL) { + return NULL; } - // put size just before data - *size_addr_from_base(ptr) = size; + // Wrap memory with fence + FencedMemory fenced(ptr, size); + ptr = fenced.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 fence memory + MemTracker::record_malloc((address)ptr, size, memflags, caller == 0 ? CALLER_PC : caller); - return memblock; + return ptr; } @@ -741,27 +641,32 @@ 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); + FencedMemory fenced(memblock); + memcpy(ptr, memblock, MIN2(size, fenced.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 @@ -769,6 +674,7 @@ 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; @@ -776,34 +682,20 @@ 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); - 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) + + FencedMemory fenced(memblock); + size_t size = fenced.get_user_size(); + inc_stat_counter(&free_bytes, size); + memblock = fenced.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) { --- /dev/null 2014-05-05 11:15:20.952377208 +0200 +++ new/src/share/vm/memory/fencedMemory.cpp 2014-05-06 10:39:53.884291270 +0200 @@ -0,0 +1,161 @@ +/* + * 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/fencedMemory.hpp" + +#include "memory/allocation.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/os.hpp" + +void* FencedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) { + size_t total_sz = FencedMemory::get_total_size(len); + void* outerp = os::malloc(total_sz, mtInternal); + if (outerp != NULL) { + FencedMemory fenced(outerp, len, tag); + void* innerp = fenced.get_user_ptr(); + memcpy(innerp, ptr, len); + return innerp; + } + return NULL; // OOM +} + +bool FencedMemory::free_copy(void* p) { + if (p == NULL) { + return true; + } + FencedMemory fenced((u_char*)p); + bool verify_ok = fenced.verify_fences(); + + /* always attempt to free, pass problem on to any nested memchecker */ + os::free(fenced.release_for_freeing()); + + return verify_ok; +} + +void FencedMemory::print_on(outputStream* st) const { + if (_base_addr == NULL) { + st->print_cr("FencedMemory(" INTPTR_FORMAT ") not associated to any memory", this); + return; + } + st->print_cr("FencedMemory(" INTPTR_FORMAT ") base_addr=" PTR_FORMAT + " tag=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT, + this, _base_addr, get_tag(), get_user_size(), get_user_ptr()); + + Fence* fence = get_head_fence(); + st->print_cr(" Header fence @" PTR_FORMAT " is %s", fence, (fence->verify() ? "OK" : "BROKEN")); + fence = get_tail_fence(); + st->print_cr(" Trailer fence @" PTR_FORMAT " is %s", fence, (fence->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"); + } +} + +// test code... + +#ifndef PRODUCT + +static void fenced_memory_test_check(void* p, size_t sz, void* tag) { + assert(p, "NULL pointer given to check"); + u_char* c = (u_char*) p; + FencedMemory fenced(c); + assert(fenced.get_tag() == tag, "Tag is not the same as supplied"); + assert(fenced.get_user_ptr() == c, "User pointer is not the same as supplied"); + assert(fenced.get_user_size() == sz, "User size is not the same as supplied"); + assert(fenced.verify_fences(), "Fence broken"); +} + +void FencedMemory::test_fenced_memory() { + // Test the basic characteristics... + size_t total_sz = FencedMemory::get_total_size(1); + assert(total_sz > 1 && total_sz >= (sizeof(FenceHeader) + 1 + sizeof(Fence)), "Unexpected size"); + u_char* basep = (u_char*) os::malloc(total_sz, mtInternal); + + FencedMemory fenced(basep, 1, (void*)0xf000f000); + + assert(*basep == badResourceValue, "Expected fence in the form of badResourceValue"); + u_char* userp = fenced.get_user_ptr(); + assert(*userp == uninitBlockPad, "Expected uninitialized data in the form of uninitBlockPad"); + fenced_memory_test_check(userp, 1, (void*)0xf000f000); + + void* freep = fenced.release_for_freeing(); + assert((u_char*)freep == basep, "Expected the same pointer fence was "); + assert(*userp == freeBlockPad, "Expected user data to be free block padded"); + assert(!fenced.verify_fences(), "Expected failed"); + os::free(freep); + + // Test a number of odd sizes... + size_t sz = 0; + do { + void* p = os::malloc(FencedMemory::get_total_size(sz), mtInternal); + void* up = fenced.wrap_with_fences(p, sz, (void*)1); + memset(up, 0, sz); + fenced_memory_test_check(up, sz, (void*)1); + os::free(fenced.release_for_freeing()); + sz = (sz << 4) + 1; + } while (sz < (256 * 1024)); + + // Test buffer overrun into head... + basep = (u_char*) os::malloc(FencedMemory::get_total_size(1), mtInternal); + fenced.wrap_with_fences(basep, 1); + *basep = 0; + assert(!fenced.verify_fences(), "Expected failure"); + os::free(basep); + + // Test buffer overrun into tail with a number of odd sizes... + sz = 1; + do { + void* p = os::malloc(FencedMemory::get_total_size(sz), mtInternal); + void* up = fenced.wrap_with_fences(p, sz, (void*)1); + memset(up, 0, sz + 1); // Buffer-overwrite (within fence) + assert(!fenced.verify_fences(), "Fence was not broken as expected"); + os::free(fenced.release_for_freeing()); + sz = (sz << 4) + 1; + } while (sz < (256 * 1024)); + + // Test wrap_copy/wrap_free... + assert(FencedMemory::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*) FencedMemory::wrap_copy(str, str_sz); + fenced_memory_test_check(str_copy, str_sz, NULL); + assert(strcmp(str, str_copy) == 0, "Not identical copy"); + assert(FencedMemory::free_copy(str_copy), "Free copy failed to verify"); + + void* no_data = NULL; + void* no_data_copy = FencedMemory::wrap_copy(no_data, 0); + assert(FencedMemory::free_copy(no_data_copy), "Expected valid fences even for no data copy"); +} + +#endif // !PRODUCT + --- /dev/null 2014-05-05 11:15:20.952377208 +0200 +++ new/src/share/vm/memory/fencedMemory.hpp 2014-05-06 10:39:54.304291290 +0200 @@ -0,0 +1,321 @@ +/* + * 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_FENCED_MEMORY_HPP +#define SHARE_VM_MEMORY_FENCED_MEMORY_HPP + +#include "memory/allocation.hpp" +#include "utilities/globalDefinitions.hpp" + +/** + * Fenced memory for detecting buffer overrun. + * + * Allows allocations to be wrapped with padded bytes of a known byte pattern, + * that is a "fence". Fence 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 fence | + * |+16 | | User data size | + * |+sizeof(uintptr_t) | | Tag word | + * |+sizeof(void*) | 0xF1 ( | User data | + * |+user_size | 0xABABABABABABABAB | Tail fence | + * ------------------------------------------------------------- + * + * Where: + * - fence 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 fence memory: + * + * Thing* alloc_thing() { + * void* mem = alloc_fn(sizeof(thing) + FencedMemory::get_total_size()); + * FencedMemory fenced(mem, sizeof(thing)); + * return (Thing*) fenced.get_user_ptr(); + * } + * + * * Verify: memory fences are still in tact + * + * bool verify_thing(Thing* thing) { + * FencedMemory fenced((void*)thing); + * return fenced.verify_fences(); + * } + * + * * Free: one may mark bytes as freed (further debugging support) + * + * void free_thing(Thing* thing) { + * FencedMemory fenced((void*)thing); + * assert(fenced.verify_fences(), "Corrupt thing"); + * free_fn(fenced.release_for_freeing(); + * } + * + */ +class FencedMemory : StackObj { // Wrapper on stack + + // Private inner classes for memory layout... + +protected: + + /** + * Fence class for header and trailer known pattern to test for overwrites. + */ + class Fence { // Class for raw memory (no vtbl allowed) + friend class FencedMemory; + protected: + static const int FENCE_SIZE = 16; + + u_char _fence[FENCE_SIZE]; + + public: + + void build() { + u_char* c = _fence; // Possibly unaligned if tail fence + u_char* end = c + FENCE_SIZE; + while (c < end) { + *c = badResourceValue; + c++; + } + } + + bool verify() const { + u_char* c = (u_char*) _fence; + u_char* end = c + FENCE_SIZE; + while (c < end) { + if (*c != badResourceValue) { + return false; + } + c++; + } + return true; + } + + }; // FencedMemory::Fence + + /** + * Header fence and size + */ + class FenceHeader : Fence { + friend class FencedMemory; + protected: + 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; } + + }; // FencedMemory::FenceHeader + + // Fenced Memory... + + protected: + u_char* _base_addr; + + public: + + /** + * Create new fenced memory. + * + * Wraps, starting at the given "base_ptr" with fences. Use "get_user_ptr()" + * to return a pointer suitable for user data. + * + * @param base_ptr allocation wishing to be wrapped, must be at least "FencedMemory::get_total_size()" bytes. + * @param user_size the size of the user data to be wrapped. + * @param tag optional general purpose tag. + */ + FencedMemory(void* base_ptr, const size_t user_size, const void* tag = NULL) { + wrap_with_fences(base_ptr, user_size, tag); + } + + /** + * Wrap existing fenced memory. + * + * To use this constructor, one must have created fenced memory with + * "FencedMemory(void*, size_t, void*)" (or indirectly via helper, e.g. "wrap_copy()"). + * + * @param user_p existing wrapped memory. + */ + FencedMemory(void* userp) { + u_char* user_ptr = (u_char*) userp; + assert((uintptr_t)user_ptr > (sizeof(FenceHeader) + 0x1000), "Invalid pointer"); + _base_addr = (user_ptr - sizeof(FenceHeader)); + } + + /** + * Create new fenced memory. + * + * Wraps, starting at the given "base_ptr" with fences. Allows reuse of stack allocated helper. + * + * @param base_ptr allocation wishing to be wrapped, must be at least "FencedMemory::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_fences(void* base_ptr, size_t user_size, const void* tag = 0) { + assert(base_ptr, "Attempt to wrap NULL with fence"); + _base_addr = (u_char*)base_ptr; + get_head_fence()->build(); + get_head_fence()->set_user_size(user_size); + get_tail_fence()->build(); + set_tag(tag); + set_user_bytes(uninitBlockPad); + assert(verify_fences(), "Expected valid fences"); + return get_user_ptr(); + } + + /** + * Verify head and tail fences. + * + * @return true if fences are intact, false would indicate a buffer overrun. + */ + bool verify_fences() const { + if (_base_addr) { + return (get_head_fence()->verify() && get_tail_fence()->verify()); + } + return false; + } + + /** + * Set the general purpose tag. + * + * @param tag general purpose tag. + */ + void set_tag(const void* tag) { get_head_fence()->set_tag(tag); } + + /** + * Return the general purpose tag. + * + * @return the general purpose tag, defaults to NULL. + */ + void* get_tag() const { return get_head_fence()->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_fence()->get_user_size(); + } + + /** + * Return the user data. + * + * @return the user data. + */ + u_char* get_user_ptr() const { + assert(_base_addr, "Not wrapping any memory"); + return _base_addr + sizeof(FenceHeader); + } + + /** + * 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: + FenceHeader* get_head_fence() const { return (FenceHeader*) _base_addr; } + Fence* get_tail_fence() const { return (Fence*) (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(FenceHeader) + user_size + sizeof(Fence); + 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 FencedMemory::get_tag()) + * + * @return fenced 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 fences were verified as intact. false indicates a buffer overrun. + */ + static bool free_copy(void* p); + + // Testing... +#ifndef PRODUCT + static void test_fenced_memory(void); +#endif +}; // FencedMemory + +#endif // SHARE_VM_MEMORY_FENCED_MEMORY_HPP