Skip to content

Commit 09cf5f1

Browse files
committed
8278602: CDS dynamic dump may access unloaded classes
Reviewed-by: coleenp, ccheung
1 parent 8dc4437 commit 09cf5f1

File tree

8 files changed

+265
-8
lines changed

8 files changed

+265
-8
lines changed

src/hotspot/share/cds/dumpTimeClassInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
#include "precompiled.hpp"
2626
#include "cds/archiveBuilder.hpp"
27-
#include "cds/dumpTimeClassInfo.hpp"
27+
#include "cds/dumpTimeClassInfo.inline.hpp"
2828
#include "classfile/classLoader.hpp"
2929
#include "classfile/classLoaderData.inline.hpp"
3030
#include "classfile/systemDictionaryShared.hpp"

src/hotspot/share/cds/dumpTimeClassInfo.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,15 @@ inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) {
168168
}
169169
}
170170

171-
class DumpTimeSharedClassTable: public ResourceHashtable<
171+
using DumpTimeSharedClassTableBaseType = ResourceHashtable<
172172
InstanceKlass*,
173173
DumpTimeClassInfo,
174174
15889, // prime number
175175
ResourceObj::C_HEAP,
176176
mtClassShared,
177-
&DumpTimeSharedClassTable_hash>
177+
&DumpTimeSharedClassTable_hash>;
178+
179+
class DumpTimeSharedClassTable: public DumpTimeSharedClassTableBaseType
178180
{
179181
int _builtin_count;
180182
int _unregistered_count;
@@ -194,6 +196,11 @@ class DumpTimeSharedClassTable: public ResourceHashtable<
194196
return _unregistered_count;
195197
}
196198
}
199+
200+
// Overrides ResourceHashtable<>::iterate(ITER*)
201+
template<class ITER> void iterate(ITER* iter) const;
202+
private:
203+
template<class ITER> class IterationHelper;
197204
};
198205

199206
#endif // SHARED_CDS_DUMPTIMESHAREDCLASSINFO_HPP
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
2+
/*
3+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*
24+
*/
25+
26+
#ifndef SHARED_CDS_DUMPTIMESHAREDCLASSINFO_INLINE_HPP
27+
#define SHARED_CDS_DUMPTIMESHAREDCLASSINFO_INLINE_HPP
28+
29+
#include "cds/dumpTimeClassInfo.hpp"
30+
#include "classfile/systemDictionaryShared.hpp"
31+
#include "classfile/classLoaderData.inline.hpp"
32+
#include "oops/instanceKlass.hpp"
33+
#include "oops/klass.inline.hpp"
34+
#include "runtime/safepoint.hpp"
35+
36+
#if INCLUDE_CDS
37+
38+
// For safety, only iterate over a class if it loader is alive.
39+
// IterationHelper and DumpTimeSharedClassTable::iterate
40+
// must be used only inside a safepoint, where the value of
41+
// k->is_loader_alive() will not change.
42+
template<class ITER>
43+
class DumpTimeSharedClassTable::IterationHelper {
44+
ITER* _iter;
45+
public:
46+
IterationHelper(ITER* iter) {
47+
_iter = iter;
48+
}
49+
bool do_entry(InstanceKlass* k, DumpTimeClassInfo& info) {
50+
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
51+
assert_lock_strong(DumpTimeTable_lock);
52+
if (k->is_loader_alive()) {
53+
bool result = _iter->do_entry(k, info);
54+
assert(k->is_loader_alive(), "must not change");
55+
return result;
56+
} else {
57+
if (!SystemDictionaryShared::is_excluded_class(k)) {
58+
SystemDictionaryShared::warn_excluded(k, "Class loader not alive");
59+
SystemDictionaryShared::set_excluded_locked(k);
60+
}
61+
return true;
62+
}
63+
}
64+
};
65+
66+
template<class ITER>
67+
void DumpTimeSharedClassTable::iterate(ITER* iter) const {
68+
IterationHelper<ITER> helper(iter);
69+
DumpTimeSharedClassTableBaseType::iterate(&helper);
70+
}
71+
72+
#endif // INCLUDE_CDS
73+
74+
#endif // SHARED_CDS_DUMPTIMESHAREDCLASSINFO_INLINE_HPP

src/hotspot/share/cds/dynamicArchive.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ class DynamicArchiveBuilder : public ArchiveBuilder {
115115
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
116116
SystemDictionaryShared::check_excluded_classes();
117117

118+
if (SystemDictionaryShared::is_dumptime_table_empty()) {
119+
log_warning(cds, dynamic)("There is no class to be included in the dynamic archive.");
120+
SystemDictionaryShared::stop_dumping();
121+
return;
122+
}
123+
118124
// save dumptime tables
119125
SystemDictionaryShared::clone_dumptime_tables();
120126

@@ -171,6 +177,7 @@ class DynamicArchiveBuilder : public ArchiveBuilder {
171177

172178
assert(_num_dump_regions_used == _total_dump_regions, "must be");
173179
verify_universe("After CDS dynamic dump");
180+
SystemDictionaryShared::stop_dumping();
174181
}
175182

176183
virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) {
@@ -342,10 +349,6 @@ class VM_PopulateDynamicDumpSharedSpace: public VM_GC_Sync_Operation {
342349
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
343350
void doit() {
344351
ResourceMark rm;
345-
if (SystemDictionaryShared::is_dumptime_table_empty()) {
346-
log_warning(cds, dynamic)("There is no class to be included in the dynamic archive.");
347-
return;
348-
}
349352
if (AllowArchivingWithJavaAgent) {
350353
warning("This archive was created with AllowArchivingWithJavaAgent. It should be used "
351354
"for testing purposes only and should not be used in a production environment");

src/hotspot/share/classfile/systemDictionaryShared.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#include "cds/filemap.hpp"
3232
#include "cds/heapShared.hpp"
3333
#include "cds/cdsProtectionDomain.hpp"
34-
#include "cds/dumpTimeClassInfo.hpp"
34+
#include "cds/dumpTimeClassInfo.inline.hpp"
3535
#include "cds/metaspaceShared.hpp"
3636
#include "cds/runTimeClassInfo.hpp"
3737
#include "classfile/classFileStream.hpp"
@@ -187,6 +187,11 @@ void SystemDictionaryShared::start_dumping() {
187187
_dump_in_progress = true;
188188
}
189189

190+
void SystemDictionaryShared::stop_dumping() {
191+
assert_lock_strong(DumpTimeTable_lock);
192+
_dump_in_progress = false;
193+
}
194+
190195
DumpTimeClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) {
191196
MutexLocker ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
192197
return find_or_allocate_info_for_locked(k);
@@ -1528,6 +1533,7 @@ void SystemDictionaryShared::print_table_statistics(outputStream* st) {
15281533
}
15291534

15301535
bool SystemDictionaryShared::is_dumptime_table_empty() {
1536+
assert_lock_strong(DumpTimeTable_lock);
15311537
if (_dumptime_table == NULL) {
15321538
return true;
15331539
}

src/hotspot/share/classfile/systemDictionaryShared.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ class SystemDictionaryShared: public SystemDictionary {
300300
static void print_table_statistics(outputStream* st) NOT_CDS_RETURN;
301301
static bool is_dumptime_table_empty() NOT_CDS_RETURN_(true);
302302
static void start_dumping() NOT_CDS_RETURN;
303+
static void stop_dumping() NOT_CDS_RETURN;
303304
static bool is_supported_invokedynamic(BootstrapInfo* bsi) NOT_CDS_RETURN_(false);
304305
DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;})
305306

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @bug 8278602
28+
* @summary Lots of classes being unloaded while we try to dump a dynamic archive
29+
* @requires vm.cds
30+
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
31+
* /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/test-classes
32+
* @build sun.hotspot.WhiteBox
33+
* @build LotsUnloadApp
34+
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
35+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar LotsUnloadApp.jar LotsUnloadApp DefinedAsHiddenKlass
36+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. LotsUnloadTest
37+
*/
38+
39+
// Note: for https://bugs.openjdk.java.net/browse/JDK-8278602, this test case does NOT
40+
// reliably reproduce the problem. Reproduction requires patching ZGC. Please see
41+
// the bug report for instructions.
42+
//
43+
// This test case is included so that it may find a similar bug under stress conditions
44+
// in the CI runs.
45+
import jdk.test.lib.helpers.ClassFileInstaller;
46+
47+
public class LotsUnloadTest extends DynamicArchiveTestBase {
48+
public static void main(String[] args) throws Exception {
49+
runTest(LotsUnloadTest::test);
50+
}
51+
52+
static void test() throws Exception {
53+
String topArchiveName = getNewArchiveName();
54+
String appJar = ClassFileInstaller.getJarPath("LotsUnloadApp.jar");
55+
String mainClass = "LotsUnloadApp";
56+
String logging;
57+
58+
if (Boolean.getBoolean("verbose.LotsUnloadTest")) {
59+
// class+unload logs may change GC timing and cause the bug to be
60+
// less reproducible.
61+
logging = "-Xlog:cds,class+unload";
62+
} else {
63+
logging = "-Xlog:cds";
64+
}
65+
66+
dump(topArchiveName,
67+
logging,
68+
"-Xmx64m", "-Xms32m",
69+
"-cp", appJar, mainClass)
70+
.assertNormalExit(output -> {
71+
output.shouldHaveExitValue(0);
72+
});
73+
74+
run(topArchiveName,
75+
logging,
76+
"-Xmx64m", "-Xms32m",
77+
"-cp", appJar, mainClass)
78+
.assertNormalExit(output -> {
79+
output.shouldHaveExitValue(0);
80+
});
81+
}
82+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
import java.lang.invoke.MethodType;
26+
import java.lang.invoke.MethodHandles;
27+
import java.lang.invoke.MethodHandles.Lookup;
28+
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
29+
30+
public class LotsUnloadApp implements Runnable {
31+
static byte[] classdata;
32+
static int exitAfterNumClasses = 1024;
33+
34+
public static void main(String args[]) throws Throwable {
35+
String resname = DefinedAsHiddenKlass.class.getName() + ".class";
36+
classdata = LotsUnloadApp.class.getClassLoader().getResourceAsStream(resname).readAllBytes();
37+
38+
int numThreads = 4;
39+
try {
40+
numThreads = Integer.parseInt(args[0]);
41+
} catch (Throwable t) {}
42+
43+
try {
44+
exitAfterNumClasses = Integer.parseInt(args[1]);
45+
} catch (Throwable t) {}
46+
47+
for (int i = 0; i < numThreads; i++) {
48+
Thread t = new Thread(new LotsUnloadApp());
49+
t.start();
50+
}
51+
}
52+
53+
public void run() {
54+
while (true) {
55+
try {
56+
Lookup lookup = MethodHandles.lookup();
57+
Class<?> cl = lookup.defineHiddenClass(classdata, false, NESTMATE).lookupClass();
58+
cl.newInstance();
59+
add();
60+
} catch (Throwable t) {
61+
t.printStackTrace();
62+
}
63+
}
64+
}
65+
66+
static int n;
67+
static synchronized void add() {
68+
n++;
69+
if (n >= exitAfterNumClasses) {
70+
System.exit(0);
71+
}
72+
}
73+
}
74+
75+
class DefinedAsHiddenKlass {
76+
// ZGC region size is always a multiple of 2MB on x64.
77+
// Make this slightly smaller than that.
78+
static byte[] array = new byte[2 * 1024 * 1024 - 8 * 1024];
79+
static String x;
80+
public DefinedAsHiddenKlass() {
81+
// This will generate some lambda forms hidden classes for string concat.
82+
x = "array size is " + array.length + " bytes ";
83+
}
84+
}

0 commit comments

Comments
 (0)