mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8369527: NMT: print malloc-site when a malloc'd memory detected as corrupted
Reviewed-by: dholmes, jsjolen
This commit is contained in:
parent
73923601d8
commit
c867971340
@ -26,6 +26,7 @@
|
||||
#include "nmt/mallocHeader.inline.hpp"
|
||||
#include "nmt/mallocSiteTable.hpp"
|
||||
#include "nmt/memTag.hpp"
|
||||
#include "nmt/memTracker.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
@ -36,7 +37,7 @@
|
||||
// fitting into eight bits.
|
||||
STATIC_ASSERT(sizeof(MemTag) == sizeof(uint8_t));
|
||||
|
||||
void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const {
|
||||
void MallocHeader::print_block_on_error(outputStream* st, address bad_address, address block_address) const {
|
||||
assert(bad_address >= (address)this, "sanity");
|
||||
|
||||
// This function prints block information, including hex dump, in case of a detected
|
||||
@ -48,6 +49,18 @@ void MallocHeader::print_block_on_error(outputStream* st, address bad_address) c
|
||||
|
||||
st->print_cr("NMT Block at " PTR_FORMAT ", corruption at: " PTR_FORMAT ": ",
|
||||
p2i(this), p2i(bad_address));
|
||||
if (MemTracker::tracking_level() == NMT_TrackingLevel::NMT_detail) {
|
||||
MallocHeader* mh = (MallocHeader*)block_address;
|
||||
NativeCallStack stack;
|
||||
if (MallocSiteTable::access_stack(stack, *mh)) {
|
||||
st->print_cr("allocated from:");
|
||||
stack.print_on(st);
|
||||
} else {
|
||||
st->print_cr("allocation-site cannot be shown since the marker is also corrupted.");
|
||||
}
|
||||
st->print_cr("");
|
||||
}
|
||||
|
||||
static const size_t min_dump_length = 256;
|
||||
address from1 = align_down((address)this, sizeof(void*)) - (min_dump_length / 2);
|
||||
address to1 = from1 + min_dump_length;
|
||||
|
||||
@ -106,7 +106,7 @@ class MallocHeader {
|
||||
// We discount sizes larger than these
|
||||
static const size_t max_reasonable_malloc_size = LP64_ONLY(256 * G) NOT_LP64(3500 * M);
|
||||
|
||||
void print_block_on_error(outputStream* st, address bad_address) const;
|
||||
void print_block_on_error(outputStream* st, address bad_address, address block_address) const;
|
||||
|
||||
static uint16_t build_footer(uint8_t b1, uint8_t b2) { return (uint16_t)(((uint16_t)b1 << 8) | (uint16_t)b2); }
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) {
|
||||
}
|
||||
OutTypeParam header_pointer = (OutTypeParam)memblock - 1;
|
||||
if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) {
|
||||
header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer);
|
||||
header_pointer->print_block_on_error(tty, corruption != nullptr ? corruption : (address)header_pointer, (address)header_pointer);
|
||||
fatal("NMT has detected a memory corruption bug. Block at " PTR_FORMAT ": %s", p2i(memblock), msg);
|
||||
}
|
||||
return header_pointer;
|
||||
|
||||
@ -163,13 +163,17 @@ MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t*
|
||||
// Access malloc site
|
||||
MallocSite* MallocSiteTable::malloc_site(uint32_t marker) {
|
||||
uint16_t bucket_idx = bucket_idx_from_marker(marker);
|
||||
assert(bucket_idx < table_size, "Invalid bucket index");
|
||||
if (bucket_idx >= table_size) {
|
||||
return nullptr;
|
||||
}
|
||||
const uint16_t pos_idx = pos_idx_from_marker(marker);
|
||||
MallocSiteHashtableEntry* head = _table[bucket_idx];
|
||||
for (size_t index = 0;
|
||||
index < pos_idx && head != nullptr;
|
||||
index++, head = (MallocSiteHashtableEntry*)head->next()) {}
|
||||
assert(head != nullptr, "Invalid position index");
|
||||
if (head == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return head->data();
|
||||
}
|
||||
|
||||
|
||||
@ -164,4 +164,14 @@ TEST_VM(NMT, test_realloc) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_VM_FATAL_ERROR_MSG(NMT, memory_corruption_call_stack, ".*header canary.*") {
|
||||
if (MemTracker::tracking_level() != NMT_detail) {
|
||||
guarantee(false, "fake message ignore this - header canary");
|
||||
}
|
||||
const size_t SIZE = 1024;
|
||||
char* p = (char*)os::malloc(SIZE, mtTest);
|
||||
*(p - 1) = 0;
|
||||
os::free(p);
|
||||
}
|
||||
|
||||
#endif // !INCLUDE_ASAN
|
||||
|
||||
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 021MALLOC_SIZE-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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Check the allocation-site stack trace of a corrupted memory at free() time
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail NMTPrintMallocSiteOfCorruptedMemory
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.whitebox.WhiteBox;
|
||||
|
||||
public class NMTPrintMallocSiteOfCorruptedMemory {
|
||||
private static final String HEADER_ARG = "header";
|
||||
private static final String FOOTER_ARG = "footer";
|
||||
private static final String HEADER_AND_SITE_ARG = "header-and-site";
|
||||
private static final String FOOTER_AND_SITE_ARG = "footer-and-site";
|
||||
private static final int MALLOC_SIZE = 10;
|
||||
private static WhiteBox wb = WhiteBox.getWhiteBox();
|
||||
|
||||
static {
|
||||
System.loadLibrary("MallocHeaderModifier");
|
||||
}
|
||||
|
||||
public static native byte modifyHeaderCanary(long malloc_memory);
|
||||
public static native byte modifyFooterCanary(long malloc_memory, long size);
|
||||
public static native byte modifyHeaderCanaryAndSiteMarker(long malloc_memory);
|
||||
public static native byte modifyFooterCanaryAndSiteMarker(long malloc_memory, long size);
|
||||
|
||||
private static void runThisTestWith(String arg) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(new String[] {"-Xbootclasspath/a:.",
|
||||
"-XX:+UnlockDiagnosticVMOptions",
|
||||
"-XX:+WhiteBoxAPI",
|
||||
"-XX:NativeMemoryTracking=detail",
|
||||
"-Djava.library.path=" + Utils.TEST_NATIVE_PATH,
|
||||
"NMTPrintMallocSiteOfCorruptedMemory",
|
||||
arg});
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
output.shouldMatch("NMT Block at .*, corruption at: ");
|
||||
switch(arg) {
|
||||
case HEADER_AND_SITE_ARG, FOOTER_AND_SITE_ARG -> output.shouldContain("allocation-site cannot be shown since the marker is also corrupted.");
|
||||
case HEADER_ARG, FOOTER_ARG -> {
|
||||
output.shouldContain("allocated from:");
|
||||
output.shouldMatch("\\[.*\\]WB_NMTMalloc\\+0x.*");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testModifyHeaderCanary() {
|
||||
long addr = wb.NMTMalloc(MALLOC_SIZE);
|
||||
modifyHeaderCanary(addr);
|
||||
wb.NMTFree(addr);
|
||||
}
|
||||
|
||||
private static void testModifyFooterCanary() {
|
||||
long addr = wb.NMTMalloc(MALLOC_SIZE);
|
||||
modifyFooterCanary(addr, MALLOC_SIZE);
|
||||
wb.NMTFree(addr);
|
||||
}
|
||||
|
||||
private static void testModifyHeaderCanaryAndSiteMarker() {
|
||||
long addr = wb.NMTMalloc(MALLOC_SIZE);
|
||||
modifyHeaderCanaryAndSiteMarker(addr);
|
||||
wb.NMTFree(addr);
|
||||
}
|
||||
|
||||
private static void testModifyFooterCanaryAndSiteMarker() {
|
||||
long addr = wb.NMTMalloc(MALLOC_SIZE);
|
||||
modifyFooterCanaryAndSiteMarker(addr, MALLOC_SIZE);
|
||||
wb.NMTFree(addr);
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (args != null && args.length == 1) {
|
||||
switch (args[0]) {
|
||||
case HEADER_ARG -> testModifyHeaderCanary();
|
||||
case FOOTER_ARG -> testModifyFooterCanary();
|
||||
case HEADER_AND_SITE_ARG -> testModifyHeaderCanaryAndSiteMarker();
|
||||
case FOOTER_AND_SITE_ARG -> testModifyFooterCanaryAndSiteMarker();
|
||||
default -> throw new RuntimeException("Invalid argument for NMTPrintMallocSiteOfCorruptedMemory (" + args[0] + ")");
|
||||
}
|
||||
} else {
|
||||
runThisTestWith(HEADER_ARG);
|
||||
runThisTestWith(FOOTER_ARG);
|
||||
runThisTestWith(HEADER_AND_SITE_ARG);
|
||||
runThisTestWith(FOOTER_AND_SITE_ARG);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
test/hotspot/jtreg/runtime/NMT/libMallocHeaderModifier.c
Normal file
52
test/hotspot/jtreg/runtime/NMT/libMallocHeaderModifier.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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 "jni.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_NMTPrintMallocSiteOfCorruptedMemory_modifyHeaderCanary(JNIEnv *env, jclass cls, jlong addr) {
|
||||
*((jint*)(uintptr_t)addr - 1) = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_NMTPrintMallocSiteOfCorruptedMemory_modifyFooterCanary(JNIEnv *env, jclass cls, jlong addr, jint size) {
|
||||
*((jbyte*)(uintptr_t)addr + size + 1) = 0;
|
||||
return 0;
|
||||
}
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_NMTPrintMallocSiteOfCorruptedMemory_modifyHeaderCanaryAndSiteMarker(JNIEnv *env, jclass cls, jlong addr) {
|
||||
jbyte* p = (jbyte*)(uintptr_t)addr - 16;
|
||||
memset(p, 0xFF , 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_NMTPrintMallocSiteOfCorruptedMemory_modifyFooterCanaryAndSiteMarker(JNIEnv *env, jclass cls, jlong addr, jint size) {
|
||||
jbyte* p = (jbyte*)(uintptr_t)addr - 16;
|
||||
memset(p, 0xFF , 16);
|
||||
*((jbyte*)(uintptr_t)addr + size + 1) = 0;
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user