Skip to content

Commit eab3a56

Browse files
committed
8377512: AOT cache creation fails with invalid native pointer
Backport-of: 00c1f4b
1 parent 120b93a commit eab3a56

5 files changed

Lines changed: 132 additions & 14 deletions

File tree

src/hotspot/share/cds/aotConstantPoolResolver.cpp

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ bool AOTConstantPoolResolver::is_resolution_deterministic(ConstantPool* cp, int
8181
bool AOTConstantPoolResolver::is_class_resolution_deterministic(InstanceKlass* cp_holder, Klass* resolved_class) {
8282
assert(!is_in_archivebuilder_buffer(cp_holder), "sanity");
8383
assert(!is_in_archivebuilder_buffer(resolved_class), "sanity");
84+
assert_at_safepoint(); // try_add_candidate() is called below and requires to be at safepoint.
8485

8586
if (resolved_class->is_instance_klass()) {
8687
InstanceKlass* ik = InstanceKlass::cast(resolved_class);
@@ -342,7 +343,15 @@ void AOTConstantPoolResolver::maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m
342343
break;
343344

344345
case Bytecodes::_invokehandle:
345-
InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK);
346+
if (CDSConfig::is_dumping_method_handles()) {
347+
ResolvedMethodEntry* method_entry = cp->resolved_method_entry_at(raw_index);
348+
int cp_index = method_entry->constant_pool_index();
349+
Symbol* sig = cp->uncached_signature_ref_at(cp_index);
350+
Klass* k;
351+
if (check_methodtype_signature(cp(), sig, &k, true)) {
352+
InterpreterRuntime::cds_resolve_invokehandle(raw_index, cp, CHECK);
353+
}
354+
}
346355
break;
347356

348357
default:
@@ -396,7 +405,7 @@ void AOTConstantPoolResolver::preresolve_indy_cp_entries(JavaThread* current, In
396405
// Check the MethodType signatures used by parameters to the indy BSMs. Make sure we don't
397406
// use types that have been excluded, or else we might end up creating MethodTypes that cannot be stored
398407
// in the AOT cache.
399-
bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret) {
408+
bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret, bool is_invokehandle) {
400409
ResourceMark rm;
401410
for (SignatureStream ss(sig); !ss.is_done(); ss.next()) {
402411
if (ss.is_reference()) {
@@ -409,11 +418,18 @@ bool AOTConstantPoolResolver::check_methodtype_signature(ConstantPool* cp, Symbo
409418
if (SystemDictionaryShared::should_be_excluded(k)) {
410419
if (log_is_enabled(Warning, aot, resolve)) {
411420
ResourceMark rm;
412-
log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name());
421+
log_warning(aot, resolve)("Cannot aot-resolve %s because %s is excluded",
422+
is_invokehandle ? "invokehandle" : "Lambda proxy",
423+
k->external_name());
413424
}
414425
return false;
415426
}
416427

428+
// cp->pool_holder() must be able to resolve k in production run
429+
precond(CDSConfig::is_dumping_aot_linked_classes());
430+
precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data()));
431+
precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data()));
432+
417433
if (ss.at_return_type() && return_type_ret != nullptr) {
418434
*return_type_ret = k;
419435
}
@@ -471,11 +487,44 @@ bool AOTConstantPoolResolver::check_lambda_metafactory_methodhandle_arg(Constant
471487
return false;
472488
}
473489

490+
// klass and sigature of the method (no need to check the method name)
474491
Symbol* sig = cp->method_handle_signature_ref_at(mh_index);
492+
Symbol* klass_name = cp->klass_name_at(cp->method_handle_klass_index_at(mh_index));
493+
475494
if (log_is_enabled(Debug, aot, resolve)) {
476495
ResourceMark rm;
477496
log_debug(aot, resolve)("Checking MethodType of MethodHandle for LambdaMetafactory BSM arg %d: %s", arg_i, sig->as_C_string());
478497
}
498+
499+
{
500+
Klass* k = find_loaded_class(Thread::current(), cp->pool_holder()->class_loader(), klass_name);
501+
if (k == nullptr) {
502+
// Dumping AOT cache: all classes should have been loaded by FinalImageRecipes::load_all_classes(). k must have
503+
// been a class that was excluded when FinalImageRecipes recorded all classes at the end of the training run.
504+
//
505+
// Dumping static CDS archive: all classes in the classlist have already been loaded, before we resolve
506+
// constants. k must have been a class that was excluded when the classlist was written
507+
// at the end of the training run.
508+
if (log_is_enabled(Warning, aot, resolve)) {
509+
ResourceMark rm;
510+
log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is not loaded", klass_name->as_C_string());
511+
}
512+
return false;
513+
}
514+
if (SystemDictionaryShared::should_be_excluded(k)) {
515+
if (log_is_enabled(Warning, aot, resolve)) {
516+
ResourceMark rm;
517+
log_warning(aot, resolve)("Cannot aot-resolve Lambda proxy because %s is excluded", k->external_name());
518+
}
519+
return false;
520+
}
521+
522+
// cp->pool_holder() must be able to resolve k in production run
523+
precond(CDSConfig::is_dumping_aot_linked_classes());
524+
precond(SystemDictionaryShared::is_builtin_loader(cp->pool_holder()->class_loader_data()));
525+
precond(SystemDictionaryShared::is_builtin_loader(k->class_loader_data()));
526+
}
527+
479528
return check_methodtype_signature(cp, sig);
480529
}
481530

src/hotspot/share/cds/aotConstantPoolResolver.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 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
@@ -74,7 +74,10 @@ class AOTConstantPoolResolver : AllStatic {
7474
static void maybe_resolve_fmi_ref(InstanceKlass* ik, Method* m, Bytecodes::Code bc, int raw_index,
7575
GrowableArray<bool>* resolve_fmi_list, TRAPS);
7676

77-
static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr);
77+
public:
78+
static bool check_methodtype_signature(ConstantPool* cp, Symbol* sig, Klass** return_type_ret = nullptr, bool is_invokehandle = false);
79+
80+
private:
7881
static bool check_lambda_metafactory_signature(ConstantPool* cp, Symbol* sig);
7982
static bool check_lambda_metafactory_methodtype_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);
8083
static bool check_lambda_metafactory_methodhandle_arg(ConstantPool* cp, int bsms_attribute_index, int arg_i);

src/hotspot/share/cds/aotMappedHeapWriter.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,8 +896,14 @@ void AOTMappedHeapWriter::compute_ptrmap(ArchiveMappedHeapInfo* heap_info) {
896896
native_ptr = RegeneratedClasses::get_regenerated_object(native_ptr);
897897
}
898898

899-
guarantee(ArchiveBuilder::current()->has_been_archived((address)native_ptr),
900-
"Metadata %p should have been archived", native_ptr);
899+
if (!ArchiveBuilder::current()->has_been_archived((address)native_ptr)) {
900+
ResourceMark rm;
901+
LogStreamHandle(Error, aot) log;
902+
log.print("Marking native pointer for oop %p (type = %s, offset = %d)",
903+
cast_from_oop<void*>(src_obj), src_obj->klass()->external_name(), field_offset);
904+
src_obj->print_on(&log);
905+
fatal("Metadata %p should have been archived", native_ptr);
906+
}
901907

902908
address buffered_native_ptr = ArchiveBuilder::current()->get_buffered_addr((address)native_ptr);
903909
address requested_native_ptr = ArchiveBuilder::current()->to_requested(buffered_native_ptr);

src/hotspot/share/oops/cpCache.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1998, 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
@@ -568,6 +568,8 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
568568
return false;
569569
}
570570

571+
int cp_index = method_entry->constant_pool_index();
572+
571573
if (!method_entry->is_resolved(Bytecodes::_invokevirtual)) {
572574
if (method_entry->method() == nullptr) {
573575
rejection_reason = "(method entry is not resolved)";
@@ -577,17 +579,31 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv
577579
rejection_reason = "(corresponding stub is generated on demand during method resolution)";
578580
return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call).
579581
}
580-
if (method_entry->is_resolved(Bytecodes::_invokehandle) && !CDSConfig::is_dumping_method_handles()) {
581-
rejection_reason = "(not dumping method handles)";
582-
return false;
582+
if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
583+
if (!CDSConfig::is_dumping_method_handles()) {
584+
rejection_reason = "(not dumping method handles)";
585+
return false;
586+
}
587+
588+
Symbol* sig = constant_pool()->uncached_signature_ref_at(cp_index);
589+
Klass* k;
590+
if (!AOTConstantPoolResolver::check_methodtype_signature(constant_pool(), sig, &k, true)) {
591+
// invokehandles that were resolved in the training run should have been filtered in
592+
// AOTConstantPoolResolver::maybe_resolve_fmi_ref so we shouldn't come to here.
593+
//
594+
// If we come here it's because the AOT assembly phase has executed an invokehandle
595+
// that uses an excluded type like jdk.jfr.Event. This should not happen because the
596+
// AOT assembly phase should execute only a very limited set of Java code.
597+
ResourceMark rm;
598+
fatal("AOT assembly phase must not resolve any invokehandles whose signatures include an excluded type");
599+
}
583600
}
584601
if (method_entry->method()->is_method_handle_intrinsic() && !CDSConfig::is_dumping_method_handles()) {
585602
rejection_reason = "(not dumping intrinsic method handles)";
586603
return false;
587604
}
588605
}
589606

590-
int cp_index = method_entry->constant_pool_index();
591607
assert(src_cp->tag_at(cp_index).is_method() || src_cp->tag_at(cp_index).is_interface_method(), "sanity");
592608

593609
if (!AOTConstantPoolResolver::is_resolution_deterministic(src_cp, cp_index)) {

test/hotspot/jtreg/runtime/cds/appcds/aotCache/ExcludedClasses.java

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 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
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar
3333
* TestApp
3434
* TestApp$Foo
35+
* TestApp$Foo$A
3536
* TestApp$Foo$Bar
3637
* TestApp$Foo$ShouldBeExcluded
3738
* TestApp$Foo$ShouldBeExcludedChild
@@ -43,6 +44,8 @@
4344
*/
4445

4546
import java.io.File;
47+
import java.lang.invoke.MethodHandle;
48+
import java.lang.invoke.MethodHandles;
4649
import java.lang.reflect.Array;
4750
import java.lang.reflect.InvocationHandler;
4851
import java.lang.reflect.Method;
@@ -165,6 +168,8 @@ static int hotSpot() {
165168
long start = System.currentTimeMillis();
166169
while (System.currentTimeMillis() - start < 1000) {
167170
lambdaHotSpot();
171+
lambdaHotSpot2();
172+
invokeHandleHotSpot();
168173
s.hotSpot2();
169174
b.hotSpot3();
170175
Taz.hotSpot4();
@@ -207,12 +212,52 @@ static void lambdaHotSpot() {
207212
}
208213
}
209214

215+
interface A {
216+
Object get();
217+
}
218+
219+
// Lambdas that refer to excluded classes should not be AOT-resolved
220+
static void lambdaHotSpot2() {
221+
long start = System.currentTimeMillis();
222+
A a = ShouldBeExcluded::new;
223+
while (System.currentTimeMillis() - start < 20) {
224+
Object obj = (ShouldBeExcluded)a.get();
225+
}
226+
}
227+
228+
static void invokeHandleHotSpot() {
229+
try {
230+
invokeHandleHotSpotImpl();
231+
} catch (Throwable t) {
232+
throw new RuntimeException("Unexpected", t);
233+
}
234+
}
235+
236+
static void invokeHandleHotSpotImpl() throws Throwable {
237+
MethodHandle constructorHandle =
238+
MethodHandles.lookup().unreflectConstructor(ShouldBeExcluded.class.getConstructor());
239+
long start = System.currentTimeMillis();
240+
while (System.currentTimeMillis() - start < 20) {
241+
// The JVM rewrites this to:
242+
// invokehandle <java/lang/invoke/MethodHandle.invoke()LShouldBeExcluded;>
243+
//
244+
// The AOT cache must not contain a java.lang.invoke.MethodType that refers to the
245+
// ShouldBeExcluded class.
246+
ShouldBeExcluded o = (ShouldBeExcluded)constructorHandle.invoke();
247+
if (o.getClass() != ShouldBeExcluded.class) {
248+
throw new RuntimeException("Unexpected object: " + o);
249+
}
250+
}
251+
}
252+
210253
static void doit(Runnable r) {
211254
r.run();
212255
}
213256

214257
// All subclasses of jdk.jfr.Event are excluded from the CDS archive.
215258
static class ShouldBeExcluded extends jdk.jfr.Event {
259+
public ShouldBeExcluded() {}
260+
216261
int f = (int)(System.currentTimeMillis()) + 123;
217262
int m() {
218263
return f + 456;
@@ -275,4 +320,3 @@ static void hotSpot4() {
275320
}
276321
}
277322
}
278-

0 commit comments

Comments
 (0)