Skip to content

Commit 30401d8

Browse files
committed
8380409: JVM crashes when -XX:AOTMode=create uses app.aotconf generated with JVMTI agent
Backport-of: 1e3fb44
1 parent 393d341 commit 30401d8

5 files changed

Lines changed: 185 additions & 2 deletions

File tree

src/hotspot/share/cds/aotArtifactFinder.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2025, 2026, 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
@@ -32,6 +32,7 @@
3232
#include "cds/lambdaProxyClassDictionary.hpp"
3333
#include "cds/regeneratedClasses.hpp"
3434
#include "classfile/systemDictionaryShared.hpp"
35+
#include "classfile/vmClasses.hpp"
3536
#include "logging/log.hpp"
3637
#include "memory/metaspaceClosure.hpp"
3738
#include "oops/instanceKlass.hpp"
@@ -169,6 +170,7 @@ void AOTArtifactFinder::find_artifacts() {
169170
end_scanning_for_oops();
170171

171172
TrainingData::cleanup_training_data();
173+
check_critical_classes();
172174
}
173175

174176
void AOTArtifactFinder::start_scanning_for_oops() {
@@ -233,6 +235,7 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) {
233235
bool created;
234236
_seen_classes->put_if_absent(ik, &created);
235237
if (created) {
238+
check_critical_class(ik);
236239
append_to_all_cached_classes(ik);
237240

238241
// All super types must be added.
@@ -310,3 +313,25 @@ void AOTArtifactFinder::all_cached_classes_do(MetaspaceClosure* it) {
310313
it->push(_all_cached_classes->adr_at(i));
311314
}
312315
}
316+
317+
void AOTArtifactFinder::check_critical_classes() {
318+
if (CDSConfig::is_dumping_static_archive()) {
319+
// vmClasses are store in the AOT cache (or AOT config file, or static archive).
320+
// If any of the vmClasses is excluded, (usually due to incompatible JVMTI agent),
321+
// the resulting cache/config/archive is unusable.
322+
for (auto id : EnumRange<vmClassID>{}) {
323+
check_critical_class(vmClasses::klass_at(id));
324+
}
325+
}
326+
}
327+
328+
void AOTArtifactFinder::check_critical_class(InstanceKlass* ik) {
329+
if (SystemDictionaryShared::is_excluded_class(ik)) {
330+
ResourceMark rm;
331+
const char* msg = err_msg("Critical class %s has been excluded. %s cannot be written.",
332+
ik->external_name(),
333+
CDSConfig::type_of_archive_being_written());
334+
AOTMetaspace::unrecoverable_writing_error(msg);
335+
}
336+
}
337+

src/hotspot/share/cds/aotArtifactFinder.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2025, 2026, 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
@@ -81,12 +81,14 @@ class AOTArtifactFinder : AllStatic {
8181
static void add_cached_type_array_class(TypeArrayKlass* tak);
8282
static void add_cached_instance_class(InstanceKlass* ik);
8383
static void append_to_all_cached_classes(Klass* k);
84+
static void check_critical_class(InstanceKlass* ik);
8485
public:
8586
static void initialize();
8687
static void find_artifacts();
8788
static void add_cached_class(Klass* k);
8889
static void add_aot_inited_class(InstanceKlass* ik);
8990
static void all_cached_classes_do(MetaspaceClosure* it);
91+
static void check_critical_classes();
9092
static void dispose();
9193
};
9294

src/hotspot/share/cds/heapShared.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra
502502
return false;
503503
}
504504

505+
AOTArtifactFinder::add_cached_class(obj->klass());
505506
AOTOopChecker::check(obj); // Make sure contents of this oop are safe.
506507
count_allocation(obj->size());
507508

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2026, 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+
/*
27+
* @test
28+
* @summary AOT training run should fail if critical classes have been redefined by JVMTI
29+
* @bug 8380409
30+
* @requires vm.cds.supports.aot.class.linking
31+
* @library /test/lib
32+
* @run driver RedefineClassHelper
33+
* @build RedefineCriticalClasses
34+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar RedefineCriticalClassesApp
35+
* @run driver RedefineCriticalClasses
36+
*/
37+
38+
import java.io.InputStream;
39+
import java.net.URL;
40+
import java.util.ArrayList;
41+
import jdk.test.lib.cds.CDSTestUtils;
42+
import jdk.test.lib.helpers.ClassFileInstaller;
43+
import jdk.test.lib.process.ProcessTools;
44+
45+
public class RedefineCriticalClasses {
46+
public static void main(String... args) throws Exception {
47+
ArrayList<String> processArgs = new ArrayList<>();
48+
49+
// redefineagent.jar is created by "@run driver RedefineClassHelper"
50+
processArgs.add("-javaagent:redefineagent.jar");
51+
52+
processArgs.add("-XX:AOTMode=record");
53+
processArgs.add("-XX:AOTConfiguration=app.aotconfig");
54+
processArgs.add("-Xlog:aot,cds");
55+
processArgs.add("-cp");
56+
processArgs.add("app.jar");
57+
processArgs.add("RedefineCriticalClassesApp");
58+
59+
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(processArgs);
60+
CDSTestUtils.executeAndLog(pb, "train")
61+
.shouldContain("Redefined: class java.lang.Class")
62+
.shouldContain("Skipping java/lang/Class: Has been redefined")
63+
.shouldContain("Critical class java.lang.Class has been excluded. AOT configuration file cannot be written")
64+
.shouldHaveExitValue(1);
65+
}
66+
}
67+
68+
class RedefineCriticalClassesApp {
69+
public static void main(String[] args) throws Exception {
70+
// Use RedefineClassHelper (loaded from redefineagent.jar into the boot class loader)
71+
// to redefine java/lang/Class, using the exact same bytecodes as from the JDK.
72+
// The JVM will mark it as having been redefined by JVMTI and will exclude it from the
73+
// AOT configuration file.
74+
75+
URL url = new URL("jrt:/java.base/java/lang/Class.class");
76+
try (InputStream in = url.openConnection().getInputStream()) {
77+
byte[] b = in.readAllBytes();
78+
System.out.println("Length = " + b.length);
79+
RedefineClassHelper.redefineClass(Class.class, b);
80+
System.out.println("Redefined: " + Class.class);
81+
}
82+
}
83+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2026, 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+
/*
27+
* @test
28+
* @summary AOT training run should fail if critical classes have been transformed by JVMTI
29+
* with ClassFileLoadHook
30+
* @bug 8380409
31+
* @requires vm.cds.supports.aot.class.linking
32+
* @library /test/lib
33+
* @build TransformCriticalClasses
34+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar TransformCriticalClassesApp
35+
* @run main/othervm/native TransformCriticalClasses
36+
*/
37+
38+
import java.util.ArrayList;
39+
import jdk.test.lib.cds.CDSTestUtils;
40+
import jdk.test.lib.helpers.ClassFileInstaller;
41+
import jdk.test.lib.process.ProcessTools;
42+
43+
public class TransformCriticalClasses {
44+
public static void main(String... args) throws Exception {
45+
ArrayList<String> processArgs = new ArrayList<>();
46+
47+
// Tell the native agent SimpleClassFileLoadHook to do an dummy transformation
48+
// of java/lang/Class. This class will be defined using the exact same bytecodes
49+
// as from the JDK, but the JVM will mark it as having been transformed by JVMTI
50+
// and will exclude it from the AOT configuration file.
51+
processArgs.add("-agentlib:SimpleClassFileLoadHook=-early,java/lang/Class,xxxxxx,xxxxxx");
52+
53+
processArgs.add("-XX:AOTMode=record");
54+
processArgs.add("-XX:AOTConfiguration=app.aotconfig");
55+
processArgs.add("-Xlog:aot,cds");
56+
processArgs.add("-cp");
57+
processArgs.add("app.jar");
58+
processArgs.add("TransformCriticalClassesApp");
59+
60+
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(processArgs);
61+
CDSTestUtils.executeAndLog(pb, "train")
62+
.shouldContain("Skipping java/lang/Class: From ClassFileLoadHook")
63+
.shouldContain("Critical class java.lang.Class has been excluded. AOT configuration file cannot be written")
64+
.shouldHaveExitValue(1);
65+
}
66+
}
67+
68+
class TransformCriticalClassesApp {
69+
public static void main(String[] args) {
70+
System.out.println("HelloWorld");
71+
}
72+
}

0 commit comments

Comments
 (0)