8292071: NMT: move MallocHeader to its own header and inline header checks
Reviewed-by: mbaesken, lucy, iklam
This commit is contained in:
parent
85a602355f
commit
3aaffd6309
66
src/hotspot/share/services/mallocHeader.cpp
Normal file
66
src/hotspot/share/services/mallocHeader.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2021, 2022 SAP SE. 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 "services/mallocHeader.inline.hpp"
|
||||||
|
|
||||||
|
#include "runtime/os.hpp"
|
||||||
|
#include "services/mallocSiteTable.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
#include "utilities/nativeCallStack.hpp"
|
||||||
|
#include "utilities/ostream.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const {
|
||||||
|
assert(bad_address >= (address)this, "sanity");
|
||||||
|
|
||||||
|
// This function prints block information, including hex dump, in case of a detected
|
||||||
|
// corruption. The hex dump should show both block header and corruption site
|
||||||
|
// (which may or may not be close together or identical). Plus some surrounding area.
|
||||||
|
//
|
||||||
|
// Note that we use os::print_hex_dump(), which is able to cope with unmapped
|
||||||
|
// memory (it uses SafeFetch).
|
||||||
|
|
||||||
|
st->print_cr("NMT Block at " PTR_FORMAT ", corruption at: " PTR_FORMAT ": ",
|
||||||
|
p2i(this), p2i(bad_address));
|
||||||
|
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;
|
||||||
|
address from2 = align_down(bad_address, sizeof(void*)) - (min_dump_length / 2);
|
||||||
|
address to2 = from2 + min_dump_length;
|
||||||
|
if (from2 > to1) {
|
||||||
|
// Dump gets too large, split up in two sections.
|
||||||
|
os::print_hex_dump(st, from1, to1, 1);
|
||||||
|
st->print_cr("...");
|
||||||
|
os::print_hex_dump(st, from2, to2, 1);
|
||||||
|
} else {
|
||||||
|
// print one hex dump
|
||||||
|
os::print_hex_dump(st, from1, to2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MallocHeader::get_stack(NativeCallStack& stack) const {
|
||||||
|
return MallocSiteTable::access_stack(stack, _mst_marker);
|
||||||
|
}
|
142
src/hotspot/share/services/mallocHeader.hpp
Normal file
142
src/hotspot/share/services/mallocHeader.hpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2021, 2022 SAP SE. 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_SERVICES_MALLOCHEADER_HPP
|
||||||
|
#define SHARE_SERVICES_MALLOCHEADER_HPP
|
||||||
|
|
||||||
|
#include "memory/allocation.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
#include "utilities/macros.hpp"
|
||||||
|
#include "utilities/nativeCallStack.hpp"
|
||||||
|
|
||||||
|
class outputStream;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Malloc tracking header.
|
||||||
|
*
|
||||||
|
* If NMT is active (state >= minimal), we need to track allocations. A simple and cheap way to
|
||||||
|
* do this is by using malloc headers.
|
||||||
|
*
|
||||||
|
* The user allocation is preceded by a header and is immediately followed by a (possibly unaligned)
|
||||||
|
* footer canary:
|
||||||
|
*
|
||||||
|
* +--------------+------------- .... ------------------+-----+
|
||||||
|
* | header | user | can |
|
||||||
|
* | | allocation | ary |
|
||||||
|
* +--------------+------------- .... ------------------+-----+
|
||||||
|
* 16 bytes user size 2 byte
|
||||||
|
*
|
||||||
|
* Alignment:
|
||||||
|
*
|
||||||
|
* The start of the user allocation needs to adhere to malloc alignment. We assume 128 bits
|
||||||
|
* on both 64-bit/32-bit to be enough for that. So the malloc header is 16 bytes long on both
|
||||||
|
* 32-bit and 64-bit.
|
||||||
|
*
|
||||||
|
* Layout on 64-bit:
|
||||||
|
*
|
||||||
|
* 0 1 2 3 4 5 6 7
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||||
|
* | 64-bit size | ...
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||||
|
*
|
||||||
|
* 8 9 10 11 12 13 14 15 16 ++
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
|
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
|
*
|
||||||
|
* Layout on 32-bit:
|
||||||
|
*
|
||||||
|
* 0 1 2 3 4 5 6 7
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||||
|
* | alt. canary | 32-bit size | ...
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
||||||
|
*
|
||||||
|
* 8 9 10 11 12 13 14 15 16 ++
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
|
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
||||||
|
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* - We have a canary in the two bytes directly preceding the user payload. That allows us to
|
||||||
|
* catch negative buffer overflows.
|
||||||
|
* - On 32-bit, due to the smaller size_t, we have some bits to spare. So we also have a second
|
||||||
|
* canary at the very start of the malloc header (generously sized 32 bits).
|
||||||
|
* - The footer canary consists of two bytes. Since the footer location may be unaligned to 16 bits,
|
||||||
|
* the bytes are stored individually.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MallocHeader {
|
||||||
|
|
||||||
|
NOT_LP64(uint32_t _alt_canary);
|
||||||
|
const size_t _size;
|
||||||
|
const uint32_t _mst_marker;
|
||||||
|
const uint8_t _flags;
|
||||||
|
const uint8_t _unused;
|
||||||
|
uint16_t _canary;
|
||||||
|
|
||||||
|
static const uint16_t _header_canary_life_mark = 0xE99E;
|
||||||
|
static const uint16_t _header_canary_dead_mark = 0xD99D;
|
||||||
|
static const uint16_t _footer_canary_life_mark = 0xE88E;
|
||||||
|
static const uint16_t _footer_canary_dead_mark = 0xD88D;
|
||||||
|
NOT_LP64(static const uint32_t _header_alt_canary_life_mark = 0xE99EE99E;)
|
||||||
|
NOT_LP64(static const uint32_t _header_alt_canary_dead_mark = 0xD88DD88D;)
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
static uint16_t build_footer(uint8_t b1, uint8_t b2) { return ((uint16_t)b1 << 8) | (uint16_t)b2; }
|
||||||
|
|
||||||
|
uint8_t* footer_address() const { return ((address)this) + sizeof(MallocHeader) + _size; }
|
||||||
|
uint16_t get_footer() const { return build_footer(footer_address()[0], footer_address()[1]); }
|
||||||
|
void set_footer(uint16_t v) { footer_address()[0] = v >> 8; footer_address()[1] = (uint8_t)v; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
inline MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, uint32_t mst_marker);
|
||||||
|
|
||||||
|
inline size_t size() const { return _size; }
|
||||||
|
inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }
|
||||||
|
inline uint32_t mst_marker() const { return _mst_marker; }
|
||||||
|
bool get_stack(NativeCallStack& stack) const;
|
||||||
|
|
||||||
|
inline void mark_block_as_dead();
|
||||||
|
|
||||||
|
// If block is broken, fill in a short descriptive text in out,
|
||||||
|
// an option pointer to the corruption in p_corruption, and return false.
|
||||||
|
// Return true if block is fine.
|
||||||
|
inline bool check_block_integrity(char* msg, size_t msglen, address* p_corruption) const;
|
||||||
|
|
||||||
|
// If block is broken, print out a report to tty (optionally with
|
||||||
|
// hex dump surrounding the broken block), then trigger a fatal error
|
||||||
|
inline void assert_block_integrity() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This needs to be true on both 64-bit and 32-bit platforms
|
||||||
|
STATIC_ASSERT(sizeof(MallocHeader) == (sizeof(uint64_t) * 2));
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SHARE_SERVICES_MALLOCHEADER_HPP
|
131
src/hotspot/share/services/mallocHeader.inline.hpp
Normal file
131
src/hotspot/share/services/mallocHeader.inline.hpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* Copyright (c) 2021, 2022 SAP SE. 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_SERVICES_MALLOCHEADER_INLINE_HPP
|
||||||
|
#define SHARE_SERVICES_MALLOCHEADER_INLINE_HPP
|
||||||
|
|
||||||
|
#include "services/mallocHeader.hpp"
|
||||||
|
|
||||||
|
#include "jvm_io.h"
|
||||||
|
#include "utilities/debug.hpp"
|
||||||
|
#include "utilities/globalDefinitions.hpp"
|
||||||
|
#include "utilities/macros.hpp"
|
||||||
|
#include "utilities/nativeCallStack.hpp"
|
||||||
|
|
||||||
|
inline MallocHeader::MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, uint32_t mst_marker)
|
||||||
|
: _size(size), _mst_marker(mst_marker), _flags(NMTUtil::flag_to_index(flags)),
|
||||||
|
_unused(0), _canary(_header_canary_life_mark)
|
||||||
|
{
|
||||||
|
assert(size < max_reasonable_malloc_size, "Too large allocation size?");
|
||||||
|
// On 32-bit we have some bits more, use them for a second canary
|
||||||
|
// guarding the start of the header.
|
||||||
|
NOT_LP64(_alt_canary = _header_alt_canary_life_mark;)
|
||||||
|
set_footer(_footer_canary_life_mark); // set after initializing _size
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MallocHeader::mark_block_as_dead() {
|
||||||
|
_canary = _header_canary_dead_mark;
|
||||||
|
NOT_LP64(_alt_canary = _header_alt_canary_dead_mark);
|
||||||
|
set_footer(_footer_canary_dead_mark);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MallocHeader::assert_block_integrity() const {
|
||||||
|
char msg[256];
|
||||||
|
address corruption = NULL;
|
||||||
|
if (!check_block_integrity(msg, sizeof(msg), &corruption)) {
|
||||||
|
if (corruption != NULL) {
|
||||||
|
print_block_on_error(tty, (address)this);
|
||||||
|
}
|
||||||
|
fatal("NMT corruption: Block at " PTR_FORMAT ": %s", p2i(this), msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool MallocHeader::check_block_integrity(char* msg, size_t msglen, address* p_corruption) const {
|
||||||
|
// Note: if you modify the error messages here, make sure you
|
||||||
|
// adapt the associated gtests too.
|
||||||
|
|
||||||
|
// Weed out obviously wrong block addresses of NULL or very low
|
||||||
|
// values. Note that we should not call this for ::free(NULL),
|
||||||
|
// which should be handled by os::free() above us.
|
||||||
|
if (((size_t)p2i(this)) < K) {
|
||||||
|
jio_snprintf(msg, msglen, "invalid block address");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From here on we assume the block pointer to be valid. We could
|
||||||
|
// use SafeFetch but since this is a hot path we don't. If we are
|
||||||
|
// wrong, we will crash when accessing the canary, which hopefully
|
||||||
|
// generates distinct crash report.
|
||||||
|
|
||||||
|
// Weed out obviously unaligned addresses. NMT blocks, being the result of
|
||||||
|
// malloc calls, should adhere to malloc() alignment. Malloc alignment is
|
||||||
|
// specified by the standard by this requirement:
|
||||||
|
// "malloc returns a pointer which is suitably aligned for any built-in type"
|
||||||
|
// For us it means that it is *at least* 64-bit on all of our 32-bit and
|
||||||
|
// 64-bit platforms since we have native 64-bit types. It very probably is
|
||||||
|
// larger than that, since there exist scalar types larger than 64bit. Here,
|
||||||
|
// we test the smallest alignment we know.
|
||||||
|
// Should we ever start using std::max_align_t, this would be one place to
|
||||||
|
// fix up.
|
||||||
|
if (!is_aligned(this, sizeof(uint64_t))) {
|
||||||
|
*p_corruption = (address)this;
|
||||||
|
jio_snprintf(msg, msglen, "block address is unaligned");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check header canary
|
||||||
|
if (_canary != _header_canary_life_mark) {
|
||||||
|
*p_corruption = (address)this;
|
||||||
|
jio_snprintf(msg, msglen, "header canary broken");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _LP64
|
||||||
|
// On 32-bit we have a second canary, check that one too.
|
||||||
|
if (_alt_canary != _header_alt_canary_life_mark) {
|
||||||
|
*p_corruption = (address)this;
|
||||||
|
jio_snprintf(msg, msglen, "header canary broken");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Does block size seems reasonable?
|
||||||
|
if (_size >= max_reasonable_malloc_size) {
|
||||||
|
*p_corruption = (address)this;
|
||||||
|
jio_snprintf(msg, msglen, "header looks invalid (weirdly large block size)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check footer canary
|
||||||
|
if (get_footer() != _footer_canary_life_mark) {
|
||||||
|
*p_corruption = footer_address();
|
||||||
|
jio_snprintf(msg, msglen, "footer canary broken at " PTR_FORMAT " (buffer overflow?)",
|
||||||
|
p2i(footer_address()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SHARE_SERVICES_MALLOCHEADER_INLINE_HPP
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "runtime/os.hpp"
|
#include "runtime/os.hpp"
|
||||||
#include "runtime/safefetch.hpp"
|
#include "runtime/safefetch.hpp"
|
||||||
|
#include "services/mallocHeader.inline.hpp"
|
||||||
#include "services/mallocSiteTable.hpp"
|
#include "services/mallocSiteTable.hpp"
|
||||||
#include "services/mallocTracker.hpp"
|
#include "services/mallocTracker.hpp"
|
||||||
#include "services/memTracker.hpp"
|
#include "services/memTracker.hpp"
|
||||||
@ -101,127 +102,12 @@ void MallocMemorySnapshot::make_adjustment() {
|
|||||||
_malloc[chunk_idx].record_free(arena_size);
|
_malloc[chunk_idx].record_free(arena_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MallocMemorySummary::initialize() {
|
void MallocMemorySummary::initialize() {
|
||||||
assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check");
|
assert(sizeof(_snapshot) >= sizeof(MallocMemorySnapshot), "Sanity Check");
|
||||||
// Uses placement new operator to initialize static area.
|
// Uses placement new operator to initialize static area.
|
||||||
::new ((void*)_snapshot)MallocMemorySnapshot();
|
::new ((void*)_snapshot)MallocMemorySnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MallocHeader::mark_block_as_dead() {
|
|
||||||
_canary = _header_canary_dead_mark;
|
|
||||||
NOT_LP64(_alt_canary = _header_alt_canary_dead_mark);
|
|
||||||
set_footer(_footer_canary_dead_mark);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MallocHeader::print_block_on_error(outputStream* st, address bad_address) const {
|
|
||||||
assert(bad_address >= (address)this, "sanity");
|
|
||||||
|
|
||||||
// This function prints block information, including hex dump, in case of a detected
|
|
||||||
// corruption. The hex dump should show both block header and corruption site
|
|
||||||
// (which may or may not be close together or identical). Plus some surrounding area.
|
|
||||||
//
|
|
||||||
// Note that we use os::print_hex_dump(), which is able to cope with unmapped
|
|
||||||
// memory (it uses SafeFetch).
|
|
||||||
|
|
||||||
st->print_cr("NMT Block at " PTR_FORMAT ", corruption at: " PTR_FORMAT ": ",
|
|
||||||
p2i(this), p2i(bad_address));
|
|
||||||
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;
|
|
||||||
address from2 = align_down(bad_address, sizeof(void*)) - (min_dump_length / 2);
|
|
||||||
address to2 = from2 + min_dump_length;
|
|
||||||
if (from2 > to1) {
|
|
||||||
// Dump gets too large, split up in two sections.
|
|
||||||
os::print_hex_dump(st, from1, to1, 1);
|
|
||||||
st->print_cr("...");
|
|
||||||
os::print_hex_dump(st, from2, to2, 1);
|
|
||||||
} else {
|
|
||||||
// print one hex dump
|
|
||||||
os::print_hex_dump(st, from1, to2, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void MallocHeader::assert_block_integrity() const {
|
|
||||||
char msg[256];
|
|
||||||
address corruption = NULL;
|
|
||||||
if (!check_block_integrity(msg, sizeof(msg), &corruption)) {
|
|
||||||
if (corruption != NULL) {
|
|
||||||
print_block_on_error(tty, (address)this);
|
|
||||||
}
|
|
||||||
fatal("NMT corruption: Block at " PTR_FORMAT ": %s", p2i(this), msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MallocHeader::check_block_integrity(char* msg, size_t msglen, address* p_corruption) const {
|
|
||||||
// Note: if you modify the error messages here, make sure you
|
|
||||||
// adapt the associated gtests too.
|
|
||||||
|
|
||||||
// Weed out obviously wrong block addresses of NULL or very low
|
|
||||||
// values. Note that we should not call this for ::free(NULL),
|
|
||||||
// which should be handled by os::free() above us.
|
|
||||||
if (((size_t)p2i(this)) < K) {
|
|
||||||
jio_snprintf(msg, msglen, "invalid block address");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From here on we assume the block pointer to be valid. We could
|
|
||||||
// use SafeFetch but since this is a hot path we don't. If we are
|
|
||||||
// wrong, we will crash when accessing the canary, which hopefully
|
|
||||||
// generates distinct crash report.
|
|
||||||
|
|
||||||
// Weed out obviously unaligned addresses. NMT blocks, being the result of
|
|
||||||
// malloc calls, should adhere to malloc() alignment. Malloc alignment is
|
|
||||||
// specified by the standard by this requirement:
|
|
||||||
// "malloc returns a pointer which is suitably aligned for any built-in type"
|
|
||||||
// For us it means that it is *at least* 64-bit on all of our 32-bit and
|
|
||||||
// 64-bit platforms since we have native 64-bit types. It very probably is
|
|
||||||
// larger than that, since there exist scalar types larger than 64bit. Here,
|
|
||||||
// we test the smallest alignment we know.
|
|
||||||
// Should we ever start using std::max_align_t, this would be one place to
|
|
||||||
// fix up.
|
|
||||||
if (!is_aligned(this, sizeof(uint64_t))) {
|
|
||||||
*p_corruption = (address)this;
|
|
||||||
jio_snprintf(msg, msglen, "block address is unaligned");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check header canary
|
|
||||||
if (_canary != _header_canary_life_mark) {
|
|
||||||
*p_corruption = (address)this;
|
|
||||||
jio_snprintf(msg, msglen, "header canary broken");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _LP64
|
|
||||||
// On 32-bit we have a second canary, check that one too.
|
|
||||||
if (_alt_canary != _header_alt_canary_life_mark) {
|
|
||||||
*p_corruption = (address)this;
|
|
||||||
jio_snprintf(msg, msglen, "header canary broken");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Does block size seems reasonable?
|
|
||||||
if (_size >= max_reasonable_malloc_size) {
|
|
||||||
*p_corruption = (address)this;
|
|
||||||
jio_snprintf(msg, msglen, "header looks invalid (weirdly large block size)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check footer canary
|
|
||||||
if (get_footer() != _footer_canary_life_mark) {
|
|
||||||
*p_corruption = footer_address();
|
|
||||||
jio_snprintf(msg, msglen, "footer canary broken at " PTR_FORMAT " (buffer overflow?)",
|
|
||||||
p2i(footer_address()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MallocHeader::get_stack(NativeCallStack& stack) const {
|
|
||||||
return MallocSiteTable::access_stack(stack, _mst_marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MallocTracker::initialize(NMT_TrackingLevel level) {
|
bool MallocTracker::initialize(NMT_TrackingLevel level) {
|
||||||
if (level >= NMT_summary) {
|
if (level >= NMT_summary) {
|
||||||
MallocMemorySummary::initialize();
|
MallocMemorySummary::initialize();
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "memory/allocation.hpp"
|
#include "memory/allocation.hpp"
|
||||||
#include "runtime/atomic.hpp"
|
#include "runtime/atomic.hpp"
|
||||||
#include "runtime/threadCritical.hpp"
|
#include "runtime/threadCritical.hpp"
|
||||||
|
#include "services/mallocHeader.hpp"
|
||||||
#include "services/nmtCommon.hpp"
|
#include "services/nmtCommon.hpp"
|
||||||
#include "utilities/nativeCallStack.hpp"
|
#include "utilities/nativeCallStack.hpp"
|
||||||
|
|
||||||
@ -236,122 +237,6 @@ class MallocMemorySummary : AllStatic {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Malloc tracking header.
|
|
||||||
*
|
|
||||||
* If NMT is active (state >= minimal), we need to track allocations. A simple and cheap way to
|
|
||||||
* do this is by using malloc headers.
|
|
||||||
*
|
|
||||||
* The user allocation is preceded by a header and is immediately followed by a (possibly unaligned)
|
|
||||||
* footer canary:
|
|
||||||
*
|
|
||||||
* +--------------+------------- .... ------------------+-----+
|
|
||||||
* | header | user | can |
|
|
||||||
* | | allocation | ary |
|
|
||||||
* +--------------+------------- .... ------------------+-----+
|
|
||||||
* 16 bytes user size 2 byte
|
|
||||||
*
|
|
||||||
* Alignment:
|
|
||||||
*
|
|
||||||
* The start of the user allocation needs to adhere to malloc alignment. We assume 128 bits
|
|
||||||
* on both 64-bit/32-bit to be enough for that. So the malloc header is 16 bytes long on both
|
|
||||||
* 32-bit and 64-bit.
|
|
||||||
*
|
|
||||||
* Layout on 64-bit:
|
|
||||||
*
|
|
||||||
* 0 1 2 3 4 5 6 7
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
|
||||||
* | 64-bit size | ...
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
|
||||||
*
|
|
||||||
* 8 9 10 11 12 13 14 15 16 ++
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
|
||||||
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
|
||||||
*
|
|
||||||
* Layout on 32-bit:
|
|
||||||
*
|
|
||||||
* 0 1 2 3 4 5 6 7
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
|
||||||
* | alt. canary | 32-bit size | ...
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+
|
|
||||||
*
|
|
||||||
* 8 9 10 11 12 13 14 15 16 ++
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
|
||||||
* ... | malloc site table marker | flags | unused | canary | ... User payload ....
|
|
||||||
* +--------+--------+--------+--------+--------+--------+--------+--------+ ------------------------
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
* - We have a canary in the two bytes directly preceding the user payload. That allows us to
|
|
||||||
* catch negative buffer overflows.
|
|
||||||
* - On 32-bit, due to the smaller size_t, we have some bits to spare. So we also have a second
|
|
||||||
* canary at the very start of the malloc header (generously sized 32 bits).
|
|
||||||
* - The footer canary consists of two bytes. Since the footer location may be unaligned to 16 bits,
|
|
||||||
* the bytes are stored individually.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class MallocHeader {
|
|
||||||
|
|
||||||
NOT_LP64(uint32_t _alt_canary);
|
|
||||||
const size_t _size;
|
|
||||||
const uint32_t _mst_marker;
|
|
||||||
const uint8_t _flags;
|
|
||||||
const uint8_t _unused;
|
|
||||||
uint16_t _canary;
|
|
||||||
|
|
||||||
static const uint16_t _header_canary_life_mark = 0xE99E;
|
|
||||||
static const uint16_t _header_canary_dead_mark = 0xD99D;
|
|
||||||
static const uint16_t _footer_canary_life_mark = 0xE88E;
|
|
||||||
static const uint16_t _footer_canary_dead_mark = 0xD88D;
|
|
||||||
NOT_LP64(static const uint32_t _header_alt_canary_life_mark = 0xE99EE99E;)
|
|
||||||
NOT_LP64(static const uint32_t _header_alt_canary_dead_mark = 0xD88DD88D;)
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
static uint16_t build_footer(uint8_t b1, uint8_t b2) { return ((uint16_t)b1 << 8) | (uint16_t)b2; }
|
|
||||||
|
|
||||||
uint8_t* footer_address() const { return ((address)this) + sizeof(MallocHeader) + _size; }
|
|
||||||
uint16_t get_footer() const { return build_footer(footer_address()[0], footer_address()[1]); }
|
|
||||||
void set_footer(uint16_t v) { footer_address()[0] = v >> 8; footer_address()[1] = (uint8_t)v; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
MallocHeader(size_t size, MEMFLAGS flags, const NativeCallStack& stack, uint32_t mst_marker)
|
|
||||||
: _size(size), _mst_marker(mst_marker), _flags(NMTUtil::flag_to_index(flags)),
|
|
||||||
_unused(0), _canary(_header_canary_life_mark)
|
|
||||||
{
|
|
||||||
assert(size < max_reasonable_malloc_size, "Too large allocation size?");
|
|
||||||
// On 32-bit we have some bits more, use them for a second canary
|
|
||||||
// guarding the start of the header.
|
|
||||||
NOT_LP64(_alt_canary = _header_alt_canary_life_mark;)
|
|
||||||
set_footer(_footer_canary_life_mark); // set after initializing _size
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t size() const { return _size; }
|
|
||||||
inline MEMFLAGS flags() const { return (MEMFLAGS)_flags; }
|
|
||||||
inline uint32_t mst_marker() const { return _mst_marker; }
|
|
||||||
bool get_stack(NativeCallStack& stack) const;
|
|
||||||
|
|
||||||
void mark_block_as_dead();
|
|
||||||
|
|
||||||
// If block is broken, fill in a short descriptive text in out,
|
|
||||||
// an option pointer to the corruption in p_corruption, and return false.
|
|
||||||
// Return true if block is fine.
|
|
||||||
bool check_block_integrity(char* msg, size_t msglen, address* p_corruption) const;
|
|
||||||
|
|
||||||
// If block is broken, print out a report to tty (optionally with
|
|
||||||
// hex dump surrounding the broken block), then trigger a fatal error
|
|
||||||
void assert_block_integrity() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This needs to be true on both 64-bit and 32-bit platforms
|
|
||||||
STATIC_ASSERT(sizeof(MallocHeader) == (sizeof(uint64_t) * 2));
|
|
||||||
|
|
||||||
|
|
||||||
// Main class called from MemTracker to track malloc activities
|
// Main class called from MemTracker to track malloc activities
|
||||||
class MallocTracker : AllStatic {
|
class MallocTracker : AllStatic {
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user