Skip to content

Commit 165dcdd

Browse files
committed
8297718: Make NMT free:ing protocol more granular
Reviewed-by: stuefe, gziemski
1 parent fbe7b00 commit 165dcdd

File tree

7 files changed

+72
-25
lines changed

7 files changed

+72
-25
lines changed

src/hotspot/share/runtime/os.cpp

+18-16
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "runtime/vm_version.hpp"
6363
#include "services/attachListener.hpp"
6464
#include "services/mallocTracker.hpp"
65+
#include "services/mallocHeader.inline.hpp"
6566
#include "services/memTracker.hpp"
6667
#include "services/nmtPreInit.hpp"
6768
#include "services/nmtCommon.hpp"
@@ -714,30 +715,31 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
714715
return NULL;
715716
}
716717

717-
const size_t old_size = MallocTracker::malloc_header(memblock)->size();
718-
719-
// De-account the old block from NMT *before* calling the real realloc(3) since it
720-
// may invalidate old block including its header. This will also perform integrity checks
721-
// on the old block (e.g. overwriters) and mark the old header as dead.
722-
void* const old_outer_ptr = MemTracker::record_free(memblock);
718+
// Perform integrity checks on and mark the old block as dead *before* calling the real realloc(3) since it
719+
// may invalidate the old block, including its header.
720+
MallocHeader* header = MallocTracker::malloc_header(memblock);
721+
header->assert_block_integrity(); // Assert block hasn't been tampered with.
722+
const MallocHeader::FreeInfo free_info = header->free_info();
723+
header->mark_block_as_dead();
723724

724725
// the real realloc
725-
ALLOW_C_FUNCTION(::realloc, void* const new_outer_ptr = ::realloc(old_outer_ptr, new_outer_size);)
726+
ALLOW_C_FUNCTION(::realloc, void* const new_outer_ptr = ::realloc(header, new_outer_size);)
726727

727728
if (new_outer_ptr == NULL) {
728-
// If realloc(3) failed, the old block still exists. We must re-instantiate the old
729-
// NMT header then, since we marked it dead already. Otherwise subsequent os::realloc()
730-
// or os::free() calls would trigger block integrity asserts.
731-
void* p = MemTracker::record_malloc(old_outer_ptr, old_size, memflags, stack);
732-
assert(p == memblock, "sanity");
733-
return NULL;
729+
// realloc(3) failed and the block still exists.
730+
// We have however marked it as dead, revert this change.
731+
header->revive();
732+
return nullptr;
734733
}
734+
// realloc(3) succeeded, variable header now points to invalid memory and we need to deaccount the old block.
735+
MemTracker::deaccount(free_info);
735736

736-
// After a successful realloc(3), we re-account the resized block with its new size
737-
// to NMT. This re-instantiates the NMT header.
737+
// After a successful realloc(3), we account the resized block with its new size
738+
// to NMT.
738739
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack);
739740

740741
#ifdef ASSERT
742+
size_t old_size = free_info.size;
741743
if (old_size < size) {
742744
// We also zap the newly extended region.
743745
::memset((char*)new_inner_ptr + old_size, uninitBlockPad, size - old_size);
@@ -774,7 +776,7 @@ void os::free(void *memblock) {
774776

775777
DEBUG_ONLY(break_if_ptr_caught(memblock);)
776778

777-
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
779+
// When NMT is enabled this checks for heap overwrites, then deaccounts the old block.
778780
void* const old_outer_ptr = MemTracker::record_free(memblock);
779781

780782
ALLOW_C_FUNCTION(::free, ::free(old_outer_ptr);)

src/hotspot/share/services/mallocHeader.hpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class outputStream;
8888
*/
8989

9090
class MallocHeader {
91-
91+
NONCOPYABLE(MallocHeader);
9292
NOT_LP64(uint32_t _alt_canary);
9393
const size_t _size;
9494
const uint32_t _mst_marker;
@@ -115,6 +115,12 @@ class MallocHeader {
115115
void set_footer(uint16_t v) { footer_address()[0] = v >> 8; footer_address()[1] = (uint8_t)v; }
116116

117117
public:
118+
// Contains all of the necessary data to to deaccount block with NMT.
119+
struct FreeInfo {
120+
const size_t size;
121+
const MEMFLAGS flags;
122+
const uint32_t mst_marker;
123+
};
118124

119125
inline MallocHeader(size_t size, MEMFLAGS flags, uint32_t mst_marker);
120126

@@ -123,7 +129,12 @@ class MallocHeader {
123129
inline uint32_t mst_marker() const { return _mst_marker; }
124130
bool get_stack(NativeCallStack& stack) const;
125131

132+
// Return the necessary data to deaccount the block with NMT.
133+
FreeInfo free_info() {
134+
return FreeInfo{this->size(), this->flags(), this->mst_marker()};
135+
}
126136
inline void mark_block_as_dead();
137+
inline void revive();
127138

128139
// If block is broken, fill in a short descriptive text in out,
129140
// an option pointer to the corruption in p_corruption, and return false.

src/hotspot/share/services/mallocHeader.inline.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ inline MallocHeader::MallocHeader(size_t size, MEMFLAGS flags, uint32_t mst_mark
4545
set_footer(_footer_canary_life_mark); // set after initializing _size
4646
}
4747

48+
inline void MallocHeader::revive() {
49+
assert(_canary == _header_canary_dead_mark, "must be dead");
50+
assert(get_footer() == _footer_canary_dead_mark, "must be dead");
51+
NOT_LP64(assert(_alt_canary == _header_alt_canary_dead_mark, "must be dead"));
52+
_canary = _header_canary_life_mark;
53+
NOT_LP64(_alt_canary = _header_alt_canary_life_mark);
54+
set_footer(_footer_canary_life_mark);
55+
}
56+
57+
// The effects of this method must be reversible with MallocHeader::revive()
4858
inline void MallocHeader::mark_block_as_dead() {
4959
_canary = _header_canary_dead_mark;
5060
NOT_LP64(_alt_canary = _header_alt_canary_dead_mark);

src/hotspot/share/services/mallocTracker.cpp

+9-5
Original file line numberDiff line numberDiff line change
@@ -180,23 +180,27 @@ void* MallocTracker::record_malloc(void* malloc_base, size_t size, MEMFLAGS flag
180180
return memblock;
181181
}
182182

183-
void* MallocTracker::record_free(void* memblock) {
183+
void* MallocTracker::record_free_block(void* memblock) {
184184
assert(MemTracker::enabled(), "Sanity");
185185
assert(memblock != NULL, "precondition");
186186

187187
MallocHeader* const header = malloc_header(memblock);
188188
header->assert_block_integrity();
189189

190-
MallocMemorySummary::record_free(header->size(), header->flags());
191-
if (MemTracker::tracking_level() == NMT_detail) {
192-
MallocSiteTable::deallocation_at(header->size(), header->mst_marker());
193-
}
190+
deaccount(header->free_info());
194191

195192
header->mark_block_as_dead();
196193

197194
return (void*)header;
198195
}
199196

197+
void MallocTracker::deaccount(MallocHeader::FreeInfo free_info) {
198+
MallocMemorySummary::record_free(free_info.size, free_info.flags);
199+
if (MemTracker::tracking_level() == NMT_detail) {
200+
MallocSiteTable::deallocation_at(free_info.size, free_info.mst_marker);
201+
}
202+
}
203+
200204
// Given a pointer, if it seems to point to the start of a valid malloced block,
201205
// print the block. Note that since there is very low risk of memory looking
202206
// accidentally like a valid malloc block header (canaries and all) this is not

src/hotspot/share/services/mallocTracker.hpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,11 @@ class MallocTracker : AllStatic {
303303
static void* record_malloc(void* malloc_base, size_t size, MEMFLAGS flags,
304304
const NativeCallStack& stack);
305305

306-
// Record free on specified memory block
307-
static void* record_free(void* memblock);
306+
// Given a block returned by os::malloc() or os::realloc():
307+
// deaccount block from NMT, mark its header as dead and return pointer to header.
308+
static void* record_free_block(void* memblock);
309+
// Given the free info from a block, de-account block from NMT.
310+
static void deaccount(MallocHeader::FreeInfo free_info);
308311

309312
static inline void record_new_arena(MEMFLAGS flags) {
310313
MallocMemorySummary::record_new_arena(flags);

src/hotspot/share/services/memTracker.hpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,11 @@ class MemTracker : AllStatic {
109109
if (!enabled()) {
110110
return memblock;
111111
}
112-
return MallocTracker::record_free(memblock);
112+
return MallocTracker::record_free_block(memblock);
113+
}
114+
static inline void deaccount(MallocHeader::FreeInfo free_info) {
115+
assert(enabled(), "NMT must be enabled");
116+
MallocTracker::deaccount(free_info);
113117
}
114118

115119
// Record creation of an arena

test/hotspot/gtest/nmt/test_nmt_cornercases.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,16 @@ TEST_VM(NMT, random_reallocs) {
141141

142142
os::free(p);
143143
}
144+
145+
TEST_VM(NMT, HeaderKeepsIntegrityAfterRevival) {
146+
if (!MemTracker::enabled()) {
147+
return;
148+
}
149+
size_t some_size = 16;
150+
void* p = os::malloc(some_size, mtTest);
151+
ASSERT_NOT_NULL(p) << "Failed to malloc()";
152+
MallocHeader* hdr = MallocTracker::malloc_header(p);
153+
hdr->mark_block_as_dead();
154+
hdr->revive();
155+
check_expected_malloc_header(p, mtTest, some_size);
156+
}

0 commit comments

Comments
 (0)