blob: 669438c85fa4e44b26eaf2533116e36f6f32cc1d [file] [log] [blame]
/*
* Copyright (c) 2014, 2023, 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, uint32_t mst_marker)
: _size(size), _mst_marker(mst_marker), _flags(flags),
_unused(0), _canary(_header_canary_live_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_live_mark;)
set_footer(_footer_canary_live_mark); // set after initializing _size
}
inline void MallocHeader::revive() {
assert(_canary == _header_canary_dead_mark, "must be dead");
assert(get_footer() == _footer_canary_dead_mark, "must be dead");
NOT_LP64(assert(_alt_canary == _header_alt_canary_dead_mark, "must be dead"));
_canary = _header_canary_live_mark;
NOT_LP64(_alt_canary = _header_alt_canary_live_mark);
set_footer(_footer_canary_live_mark);
}
// The effects of this method must be reversible with MallocHeader::revive()
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 bool MallocHeader::is_valid_malloced_pointer(const void* payload, char* msg, size_t msglen) {
// Handle the pointer as an integral type
uintptr_t ptr = reinterpret_cast<uintptr_t>(payload);
// Weed out obviously wrong block addresses of null or very low
// values. Note that we should not call this for ::free(nullptr),
// which should be handled by os::free() above us.
if (ptr < 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(ptr, sizeof(uint64_t))) {
jio_snprintf(msg, msglen, "block address is unaligned");
return false;
}
return true;
}
template<typename InTypeParam, typename OutTypeParam>
inline OutTypeParam MallocHeader::resolve_checked_impl(InTypeParam memblock) {
char msg[256];
address corruption = nullptr;
if (!is_valid_malloced_pointer(memblock, msg, sizeof(msg))) {
fatal("Not a valid malloc pointer: " PTR_FORMAT ": %s", p2i(memblock), msg);
}
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);
fatal("NMT corruption: Block at " PTR_FORMAT ": %s", p2i(memblock), msg);
}
return header_pointer;
}
inline MallocHeader* MallocHeader::resolve_checked(void* memblock) {
return MallocHeader::resolve_checked_impl<void*, MallocHeader*>(memblock);
}
inline const MallocHeader* MallocHeader::resolve_checked(const void* memblock) {
return MallocHeader::resolve_checked_impl<const void*, const MallocHeader*>(memblock);
}
// Used for debugging purposes only. Check header if it could constitute a valid (live or dead) header.
inline bool MallocHeader::looks_valid() const {
// Note: we define these restrictions loose enough to also catch moderately corrupted blocks.
// E.g. we don't check footer canary.
return ( (_canary == _header_canary_live_mark NOT_LP64(&& _alt_canary == _header_alt_canary_live_mark)) ||
(_canary == _header_canary_dead_mark NOT_LP64(&& _alt_canary == _header_alt_canary_dead_mark)) ) &&
_size > 0 && _size < max_reasonable_malloc_size;
}
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.
// Check header canary
if (_canary != _header_canary_live_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_live_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_live_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