Skip to content

Commit a70eba8

Browse files
tstuefeiklam
andcommitted
8330174: Protection zone for easier detection of accidental zero-nKlass use
Co-authored-by: Ioi Lam <[email protected]> Reviewed-by: iklam, rkennke
1 parent f529bf7 commit a70eba8

File tree

16 files changed

+397
-95
lines changed

16 files changed

+397
-95
lines changed

src/hotspot/share/cds/archiveBuilder.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,14 @@ void ArchiveBuilder::SourceObjList::relocate(int i, ArchiveBuilder* builder) {
153153
ArchiveBuilder::ArchiveBuilder() :
154154
_current_dump_region(nullptr),
155155
_buffer_bottom(nullptr),
156-
_num_dump_regions_used(0),
157156
_requested_static_archive_bottom(nullptr),
158157
_requested_static_archive_top(nullptr),
159158
_requested_dynamic_archive_bottom(nullptr),
160159
_requested_dynamic_archive_top(nullptr),
161160
_mapped_static_archive_bottom(nullptr),
162161
_mapped_static_archive_top(nullptr),
163162
_buffer_to_requested_delta(0),
163+
_pz_region("pz", MAX_SHARED_DELTA), // protection zone -- used only during dumping; does NOT exist in cds archive.
164164
_rw_region("rw", MAX_SHARED_DELTA),
165165
_ro_region("ro", MAX_SHARED_DELTA),
166166
_ptrmap(mtClassShared),
@@ -323,9 +323,14 @@ address ArchiveBuilder::reserve_buffer() {
323323
_shared_rs = rs;
324324

325325
_buffer_bottom = buffer_bottom;
326-
_current_dump_region = &_rw_region;
327-
_num_dump_regions_used = 1;
328-
_current_dump_region->init(&_shared_rs, &_shared_vs);
326+
327+
if (CDSConfig::is_dumping_static_archive()) {
328+
_current_dump_region = &_pz_region;
329+
_current_dump_region->init(&_shared_rs, &_shared_vs);
330+
} else {
331+
_current_dump_region = &_rw_region;
332+
_current_dump_region->init(&_shared_rs, &_shared_vs);
333+
}
329334

330335
ArchivePtrMarker::initialize(&_ptrmap, &_shared_vs);
331336

@@ -366,7 +371,8 @@ address ArchiveBuilder::reserve_buffer() {
366371
if (CDSConfig::is_dumping_static_archive()) {
367372
// We don't want any valid object to be at the very bottom of the archive.
368373
// See ArchivePtrMarker::mark_pointer().
369-
rw_region()->allocate(16);
374+
_pz_region.allocate(MetaspaceShared::protection_zone_size());
375+
start_dump_region(&_rw_region);
370376
}
371377

372378
return buffer_bottom;
@@ -544,7 +550,6 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref
544550
void ArchiveBuilder::start_dump_region(DumpRegion* next) {
545551
current_dump_region()->pack(next);
546552
_current_dump_region = next;
547-
_num_dump_regions_used ++;
548553
}
549554

550555
char* ArchiveBuilder::ro_strdup(const char* s) {

src/hotspot/share/cds/archiveBuilder.hpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -96,7 +96,6 @@ class ArchiveBuilder : public StackObj {
9696
protected:
9797
DumpRegion* _current_dump_region;
9898
address _buffer_bottom; // for writing the contents of rw/ro regions
99-
int _num_dump_regions_used;
10099

101100
// These are the addresses where we will request the static and dynamic archives to be
102101
// mapped at run time. If the request fails (due to ASLR), we will map the archives at
@@ -210,6 +209,12 @@ class ArchiveBuilder : public StackObj {
210209
ReservedSpace _shared_rs;
211210
VirtualSpace _shared_vs;
212211

212+
// The "pz" region is used only during static dumps to reserve an unused space between SharedBaseAddress and
213+
// the bottom of the rw region. During runtime, this space will be filled with a reserved area that disallows
214+
// read/write/exec, so we can track for bad CompressedKlassPointers encoding.
215+
// Note: this region does NOT exist in the cds archive.
216+
DumpRegion _pz_region;
217+
213218
DumpRegion _rw_region;
214219
DumpRegion _ro_region;
215220

@@ -270,9 +275,6 @@ class ArchiveBuilder : public StackObj {
270275

271276
protected:
272277
virtual void iterate_roots(MetaspaceClosure* it) = 0;
273-
274-
static const int _total_dump_regions = 2;
275-
276278
void start_dump_region(DumpRegion* next);
277279

278280
public:
@@ -367,6 +369,7 @@ class ArchiveBuilder : public StackObj {
367369
void remember_embedded_pointer_in_enclosing_obj(MetaspaceClosure::Ref* ref);
368370
static void serialize_dynamic_archivable_items(SerializeClosure* soc);
369371

372+
DumpRegion* pz_region() { return &_pz_region; }
370373
DumpRegion* rw_region() { return &_rw_region; }
371374
DumpRegion* ro_region() { return &_ro_region; }
372375

src/hotspot/share/cds/archiveUtils.cpp

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,34 +73,39 @@ void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, VirtualSpace* vs) {
7373
}
7474

7575
void ArchivePtrMarker::initialize_rw_ro_maps(CHeapBitMap* rw_ptrmap, CHeapBitMap* ro_ptrmap) {
76-
address* rw_bottom = (address*)ArchiveBuilder::current()->rw_region()->base();
77-
address* ro_bottom = (address*)ArchiveBuilder::current()->ro_region()->base();
76+
address* buff_bottom = (address*)ArchiveBuilder::current()->buffer_bottom();
77+
address* rw_bottom = (address*)ArchiveBuilder::current()->rw_region()->base();
78+
address* ro_bottom = (address*)ArchiveBuilder::current()->ro_region()->base();
7879

79-
_rw_ptrmap = rw_ptrmap;
80-
_ro_ptrmap = ro_ptrmap;
80+
// The bit in _ptrmap that cover the very first word in the rw/ro regions.
81+
size_t rw_start = rw_bottom - buff_bottom;
82+
size_t ro_start = ro_bottom - buff_bottom;
8183

84+
// The number of bits used by the rw/ro ptrmaps. We might have lots of zero
85+
// bits at the bottom and top of rrw/ro ptrmaps, but these zeros will be
86+
// removed by FileMapInfo::write_bitmap_region().
8287
size_t rw_size = ArchiveBuilder::current()->rw_region()->used() / sizeof(address);
8388
size_t ro_size = ArchiveBuilder::current()->ro_region()->used() / sizeof(address);
84-
// ro_start is the first bit in _ptrmap that covers the pointer that would sit at ro_bottom.
85-
// E.g., if rw_bottom = (address*)100
86-
// ro_bottom = (address*)116
87-
// then for 64-bit platform:
88-
// ro_start = ro_bottom - rw_bottom = (116 - 100) / sizeof(address) = 2;
89-
size_t ro_start = ro_bottom - rw_bottom;
90-
91-
// Note: ptrmap is big enough only to cover the last pointer in ro_region.
92-
// See ArchivePtrMarker::compact()
93-
_rw_ptrmap->initialize(rw_size);
94-
_ro_ptrmap->initialize(_ptrmap->size() - ro_start);
95-
96-
for (size_t rw_bit = 0; rw_bit < _rw_ptrmap->size(); rw_bit++) {
97-
_rw_ptrmap->at_put(rw_bit, _ptrmap->at(rw_bit));
89+
90+
// The last (exclusive) bit in _ptrmap that covers the rw/ro regions.
91+
// Note: _ptrmap is dynamically expanded only when an actual pointer is written, so
92+
// it may not be as large as we want.
93+
size_t rw_end = MIN2<size_t>(rw_start + rw_size, _ptrmap->size());
94+
size_t ro_end = MIN2<size_t>(ro_start + ro_size, _ptrmap->size());
95+
96+
rw_ptrmap->initialize(rw_size);
97+
ro_ptrmap->initialize(ro_size);
98+
99+
for (size_t rw_bit = rw_start; rw_bit < rw_end; rw_bit++) {
100+
rw_ptrmap->at_put(rw_bit - rw_start, _ptrmap->at(rw_bit));
98101
}
99102

100-
for(size_t ro_bit = ro_start; ro_bit < _ptrmap->size(); ro_bit++) {
101-
_ro_ptrmap->at_put(ro_bit-ro_start, _ptrmap->at(ro_bit));
103+
for(size_t ro_bit = ro_start; ro_bit < ro_end; ro_bit++) {
104+
ro_ptrmap->at_put(ro_bit - ro_start, _ptrmap->at(ro_bit));
102105
}
103-
assert(_ptrmap->size() - ro_start == _ro_ptrmap->size(), "must be");
106+
107+
_rw_ptrmap = rw_ptrmap;
108+
_ro_ptrmap = ro_ptrmap;
104109
}
105110

106111
void ArchivePtrMarker::mark_pointer(address* ptr_loc) {

src/hotspot/share/cds/dynamicArchive.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ class DynamicArchiveBuilder : public ArchiveBuilder {
170170

171171
post_dump();
172172

173-
assert(_num_dump_regions_used == _total_dump_regions, "must be");
174173
verify_universe("After CDS dynamic dump");
175174
}
176175

src/hotspot/share/cds/filemap.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ class FileMapInfo : public CHeapObj<mtInternal> {
400400
// The offset of the (exclusive) end of the last core region in this archive, relative to SharedBaseAddress
401401
size_t mapping_end_offset() const { return last_core_region()->mapping_end_offset(); }
402402

403-
char* mapped_base() const { return first_core_region()->mapped_base(); }
403+
char* mapped_base() const { return header()->mapped_base_address(); }
404404
char* mapped_end() const { return last_core_region()->mapped_end(); }
405405

406406
// Non-zero if the archive needs to be mapped a non-default location due to ASLR.

src/hotspot/share/cds/metaspaceShared.cpp

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ size_t MetaspaceShared::core_region_alignment() {
147147
return os::cds_core_region_alignment();
148148
}
149149

150+
size_t MetaspaceShared::protection_zone_size() {
151+
return os::cds_core_region_alignment();
152+
}
153+
150154
static bool shared_base_valid(char* shared_base) {
151155
// We check user input for SharedBaseAddress at dump time.
152156

@@ -1280,6 +1284,7 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
12801284

12811285
ReservedSpace total_space_rs, archive_space_rs, class_space_rs;
12821286
MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE;
1287+
size_t prot_zone_size = 0;
12831288
char* mapped_base_address = reserve_address_space_for_archives(static_mapinfo,
12841289
dynamic_mapinfo,
12851290
use_requested_addr,
@@ -1291,14 +1296,29 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
12911296
log_debug(cds)("Failed to reserve spaces (use_requested_addr=%u)", (unsigned)use_requested_addr);
12921297
} else {
12931298

1299+
if (Metaspace::using_class_space()) {
1300+
prot_zone_size = protection_zone_size();
1301+
#ifdef ASSERT
1302+
// Before mapping the core regions into the newly established address space, we mark
1303+
// start and the end of the future protection zone with canaries. That way we easily
1304+
// catch mapping errors (accidentally mapping data into the future protection zone).
1305+
os::commit_memory(mapped_base_address, prot_zone_size, false);
1306+
*(mapped_base_address) = 'P';
1307+
*(mapped_base_address + prot_zone_size - 1) = 'P';
1308+
#endif
1309+
}
1310+
12941311
#ifdef ASSERT
12951312
// Some sanity checks after reserving address spaces for archives
12961313
// and class space.
12971314
assert(archive_space_rs.is_reserved(), "Sanity");
12981315
if (Metaspace::using_class_space()) {
1316+
assert(archive_space_rs.base() == mapped_base_address &&
1317+
archive_space_rs.size() > protection_zone_size(),
1318+
"Archive space must lead and include the protection zone");
12991319
// Class space must closely follow the archive space. Both spaces
13001320
// must be aligned correctly.
1301-
assert(class_space_rs.is_reserved(),
1321+
assert(class_space_rs.is_reserved() && class_space_rs.size() > 0,
13021322
"A class space should have been reserved");
13031323
assert(class_space_rs.base() >= archive_space_rs.end(),
13041324
"class space should follow the cds archive space");
@@ -1311,8 +1331,9 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
13111331
}
13121332
#endif // ASSERT
13131333

1314-
log_info(cds)("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (%zu) bytes",
1315-
p2i(archive_space_rs.base()), p2i(archive_space_rs.end()), archive_space_rs.size());
1334+
log_info(cds)("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (%zu) bytes%s",
1335+
p2i(archive_space_rs.base()), p2i(archive_space_rs.end()), archive_space_rs.size(),
1336+
(prot_zone_size > 0 ? " (includes protection zone)" : ""));
13161337
log_info(cds)("Reserved class_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (%zu) bytes",
13171338
p2i(class_space_rs.base()), p2i(class_space_rs.end()), class_space_rs.size());
13181339

@@ -1384,38 +1405,40 @@ MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, File
13841405
if (result == MAP_ARCHIVE_SUCCESS) {
13851406
SharedBaseAddress = (size_t)mapped_base_address;
13861407
#ifdef _LP64
1387-
if (Metaspace::using_class_space()) {
1388-
// Set up ccs in metaspace.
1389-
Metaspace::initialize_class_space(class_space_rs);
1390-
1391-
// Set up compressed Klass pointer encoding: the encoding range must
1392-
// cover both archive and class space.
1393-
address cds_base = (address)static_mapinfo->mapped_base();
1394-
address ccs_end = (address)class_space_rs.end();
1395-
assert(ccs_end > cds_base, "Sanity check");
1396-
if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) {
1397-
// The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time:
1398-
// - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP)
1399-
// - every archived Klass' prototype (only if +UseCompactObjectHeaders)
1400-
//
1401-
// In order for those IDs to still be valid, we need to dictate base and shift: base should be the
1402-
// mapping start, shift the shift used at archive generation time.
1403-
address precomputed_narrow_klass_base = cds_base;
1404-
const int precomputed_narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift();
1405-
CompressedKlassPointers::initialize_for_given_encoding(
1406-
cds_base, ccs_end - cds_base, // Klass range
1407-
precomputed_narrow_klass_base, precomputed_narrow_klass_shift // precomputed encoding, see ArchiveBuilder
1408-
);
1409-
} else {
1410-
// Let JVM freely chose encoding base and shift
1411-
CompressedKlassPointers::initialize (
1412-
cds_base, ccs_end - cds_base // Klass range
1413-
);
1414-
}
1415-
// map_or_load_heap_region() compares the current narrow oop and klass encodings
1416-
// with the archived ones, so it must be done after all encodings are determined.
1417-
static_mapinfo->map_or_load_heap_region();
1418-
}
1408+
if (Metaspace::using_class_space()) {
1409+
assert(*(mapped_base_address) == 'P' &&
1410+
*(mapped_base_address + prot_zone_size - 1) == 'P',
1411+
"Protection zone was overwritten?");
1412+
1413+
// Set up ccs in metaspace.
1414+
Metaspace::initialize_class_space(class_space_rs);
1415+
1416+
// Set up compressed Klass pointer encoding: the encoding range must
1417+
// cover both archive and class space.
1418+
const address encoding_base = (address)mapped_base_address;
1419+
const address klass_range_start = encoding_base + prot_zone_size;
1420+
const size_t klass_range_size = (address)class_space_rs.end() - klass_range_start;
1421+
if (INCLUDE_CDS_JAVA_HEAP || UseCompactObjectHeaders) {
1422+
// The CDS archive may contain narrow Klass IDs that were precomputed at archive generation time:
1423+
// - every archived java object header (only if INCLUDE_CDS_JAVA_HEAP)
1424+
// - every archived Klass' prototype (only if +UseCompactObjectHeaders)
1425+
//
1426+
// In order for those IDs to still be valid, we need to dictate base and shift: base should be the
1427+
// mapping start (including protection zone), shift should be the shift used at archive generation time.
1428+
CompressedKlassPointers::initialize_for_given_encoding(
1429+
klass_range_start, klass_range_size,
1430+
encoding_base, ArchiveBuilder::precomputed_narrow_klass_shift() // precomputed encoding, see ArchiveBuilder
1431+
);
1432+
} else {
1433+
// Let JVM freely chose encoding base and shift
1434+
CompressedKlassPointers::initialize(klass_range_start, klass_range_size);
1435+
}
1436+
CompressedKlassPointers::establish_protection_zone(encoding_base, prot_zone_size);
1437+
1438+
// map_or_load_heap_region() compares the current narrow oop and klass encodings
1439+
// with the archived ones, so it must be done after all encodings are determined.
1440+
static_mapinfo->map_or_load_heap_region();
1441+
}
14191442
#endif // _LP64
14201443
log_info(cds)("initial optimized module handling: %s", CDSConfig::is_using_optimized_module_handling() ? "enabled" : "disabled");
14211444
log_info(cds)("initial full module graph: %s", CDSConfig::is_using_full_module_graph() ? "enabled" : "disabled");
@@ -1497,7 +1520,6 @@ char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_ma
14971520
const size_t archive_space_alignment = core_region_alignment();
14981521

14991522
// Size and requested location of the archive_space_rs (for both static and dynamic archives)
1500-
assert(static_mapinfo->mapping_base_offset() == 0, "Must be");
15011523
size_t archive_end_offset = (dynamic_mapinfo == nullptr) ? static_mapinfo->mapping_end_offset() : dynamic_mapinfo->mapping_end_offset();
15021524
size_t archive_space_size = align_up(archive_end_offset, archive_space_alignment);
15031525

src/hotspot/share/cds/metaspaceShared.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class MetaspaceShared : AllStatic {
139139
// Alignment for the 2 core CDS regions (RW/RO) only.
140140
// (Heap region alignments are decided by GC).
141141
static size_t core_region_alignment();
142+
static size_t protection_zone_size();
142143
static void rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread, InstanceKlass* ik);
143144
// print loaded classes names to file.
144145
static void dump_loaded_classes(const char* file_name, TRAPS);

src/hotspot/share/include/cds.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ typedef struct CDSFileMapRegion {
7070
size_t _ptrmap_offset; // Bitmap for relocating native pointer fields in archived heap objects.
7171
// (The base address is the bottom of the BM region).
7272
size_t _ptrmap_size_in_bits;
73-
char* _mapped_base; // Actually mapped address (null if this region is not mapped).
73+
char* _mapped_base; // Actually mapped address used for mapping the core regions. At that address the
74+
// zero nklass protection zone is established; following that (at offset
75+
// MetaspaceShared::protection_zone_size()) the lowest core region (rw for the
76+
// static archive) is is mapped.
7477
bool _in_reserved_space; // Is this region in a ReservedSpace
7578
} CDSFileMapRegion;
7679

0 commit comments

Comments
 (0)