/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/hotspot/share/nmt/mallocHeader.inline.hpp
181 строка
7 KB
Afshin Zafari
8369393: NMT: poison the malloc header and footer under ASAN build
04 фев 2026, 12:13
04 фев 2026, 12:13
651e01b
Код
Авторство
О чём код?
/* * Copyright (c) 2014, 2025, 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_NMT_MALLOCHEADER_INLINE_HPP #define SHARE_NMT_MALLOCHEADER_INLINE_HPP #include "nmt/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, MemTag mem_tag, uint32_t mst_marker) : _size(size), _mst_marker(mst_marker), _mem_tag(mem_tag), _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() { _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) { OutTypeParam header_pointer = (OutTypeParam)memblock - 1; #ifdef NMT_BLOCK_INTEGRITY_CHECKS 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); } if (!header_pointer->check_block_integrity(msg, sizeof(msg), &corruption)) { 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); } #endif 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; } MallocHeader* MallocHeader::kill_block(void* memblock) { MallocHeader* header = (MallocHeader*)memblock - 1; ASAN_UNPOISON_MEMORY_REGION(header, sizeof(MallocHeader)); ASAN_UNPOISON_MEMORY_REGION(header->footer_address(), footer_size); resolve_checked(memblock); header->mark_block_as_dead(); return header; } void MallocHeader::revive_block(void* memblock) { MallocHeader* header = (MallocHeader*)memblock - 1; header->revive(); ASAN_POISON_MEMORY_REGION(header->footer_address(), footer_size); ASAN_POISON_MEMORY_REGION(header, sizeof(MallocHeader)); } #endif // SHARE_NMT_MALLOCHEADER_INLINE_HPP