| /* |
| * 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 |