1 /*
2 * Copyright (c) 2022, 2023, 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 #include "precompiled.hpp"
26 #include "cds/archiveBuilder.hpp"
27 #include "cds/cdsHeapVerifier.hpp"
28 #include "classfile/classLoaderDataGraph.hpp"
29 #include "classfile/javaClasses.inline.hpp"
30 #include "logging/log.hpp"
31 #include "logging/logStream.hpp"
32 #include "memory/resourceArea.hpp"
33 #include "oops/fieldStreams.inline.hpp"
34 #include "oops/klass.inline.hpp"
35 #include "oops/oop.inline.hpp"
36 #include "runtime/fieldDescriptor.inline.hpp"
37
38 #if INCLUDE_CDS_JAVA_HEAP
39
40 // CDSHeapVerifier is used to check for problems where an archived object references a
41 // static field that may be reinitialized at runtime. In the following example,
42 // Foo.get.test()
43 // correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled.
44 //
45 // class Foo {
106 // tested for equality.
107 ADD_EXCL("java/util/HashSet", "PRESENT"); // E
108 ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
109 ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A
110 "APP_LOADER", // A
111 "PLATFORM_LOADER"); // A
112 ADD_EXCL("jdk/internal/module/Builder", "cachedVersion"); // D
113 ADD_EXCL("jdk/internal/module/ModuleLoaderMap$Mapper", "APP_CLASSLOADER", // A
114 "APP_LOADER_INDEX", // A
115 "PLATFORM_CLASSLOADER", // A
116 "PLATFORM_LOADER_INDEX"); // A
117 ADD_EXCL("jdk/internal/module/ServicesCatalog", "CLV"); // A
118
119 // This just points to an empty Map
120 ADD_EXCL("jdk/internal/reflect/Reflection", "methodFilterMap"); // E
121
122 // Integer for 0 and 1 are in java/lang/Integer$IntegerCache and are archived
123 ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
124 "ZERO_INT"); // E
125
126 # undef ADD_EXCL
127
128 ClassLoaderDataGraph::classes_do(this);
129 }
130
131 CDSHeapVerifier::~CDSHeapVerifier() {
132 if (_problems > 0) {
133 log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where "
134 "an object points to a static field that may be "
135 "reinitialized at runtime.", _archived_objs, _problems);
136 }
137 }
138
139 class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
140 CDSHeapVerifier* _verifier;
141 InstanceKlass* _ik;
142 const char** _exclusions;
143 public:
144 CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
145 : _verifier(verifier), _ik(ik) {
146 _exclusions = _verifier->find_exclusion(_ik);
147 }
148
149 void do_field(fieldDescriptor* fd) {
150 if (fd->field_type() != T_OBJECT) {
151 return;
152 }
153
154 oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
155 if (static_obj_field != nullptr) {
156 Klass* klass = static_obj_field->klass();
157 if (_exclusions != nullptr) {
158 for (const char** p = _exclusions; *p != nullptr; p++) {
159 if (fd->name()->equals(*p)) {
160 return;
161 }
162 }
163 }
164
165 if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
166 // This field looks like like this in the Java source:
167 // static final SOME_STRING = "a string literal";
168 // This string literal has been stored in the shared string table, so it's OK
169 // for the archived objects to refer to it.
170 return;
171 }
172 if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
173 // This field points to an archived mirror.
174 return;
175 }
176 if (klass->has_archived_enum_objs()) {
177 // This klass is a subclass of java.lang.Enum. If any instance of this klass
178 // has been archived, we will archive all static fields of this klass.
179 // See HeapShared::initialize_enum_klass().
180 return;
181 }
182
183 // This field *may* be initialized to a different value at runtime. Remember it
184 // and check later if it appears in the archived object graph.
185 _verifier->add_static_obj_field(_ik, static_obj_field, fd->name());
186 }
187 }
188 };
189
190 // Remember all the static object fields of every class that are currently
191 // loaded.
192 void CDSHeapVerifier::do_klass(Klass* k) {
193 if (k->is_instance_klass()) {
194 InstanceKlass* ik = InstanceKlass::cast(k);
195
196 if (HeapShared::is_subgraph_root_class(ik)) {
197 // ik is inside one of the ArchivableStaticFieldInfo tables
198 // in heapShared.cpp. We assume such classes are programmed to
199 // update their static fields correctly at runtime.
200 return;
201 }
202
203 CheckStaticFields csf(this, ik);
204 ik->do_local_static_fields(&csf);
205 }
206 }
207
208 void CDSHeapVerifier::add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name) {
209 StaticFieldInfo info = {ik, name};
210 _table.put(field, info);
211 }
212
213 inline bool CDSHeapVerifier::do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value) {
214 _archived_objs++;
215
216 StaticFieldInfo* info = _table.get(orig_obj);
217 if (info != nullptr) {
218 if (value.orig_referrer() == nullptr && java_lang_String::is_instance(orig_obj)) {
219 // This string object is not referenced by any of the archived object graphs. It's archived
220 // only because it's in the interned string table. So we are not in a condition that
221 // should be flagged by CDSHeapVerifier.
222 return true; /* keep on iterating */
223 }
224 ResourceMark rm;
225 LogStream ls(Log(cds, heap)::warning());
226 ls.print_cr("Archive heap points to a static field that may be reinitialized at runtime:");
227 ls.print_cr("Field: %s::%s", info->_holder->name()->as_C_string(), info->_name->as_C_string());
228 ls.print("Value: ");
229 orig_obj->print_on(&ls);
230 ls.print_cr("--- trace begin ---");
231 trace_to_root(&ls, orig_obj, nullptr, &value);
232 ls.print_cr("--- trace end ---");
233 ls.cr();
234 _problems ++;
235 }
236
237 return true; /* keep on iterating */
238 }
239
240 class CDSHeapVerifier::TraceFields : public FieldClosure {
241 oop _orig_obj;
242 oop _orig_field;
243 outputStream* _st;
244
245 public:
246 TraceFields(oop orig_obj, oop orig_field, outputStream* st)
247 : _orig_obj(orig_obj), _orig_field(orig_field), _st(st) {}
263 trace_to_root(st, orig_obj, nullptr, info);
264 } else {
265 st->print_cr("Not an archived object??");
266 }
267 }
268
269 int CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* info) {
270 int level = 0;
271 if (info->orig_referrer() != nullptr) {
272 HeapShared::CachedOopInfo* ref = HeapShared::archived_object_cache()->get(info->orig_referrer());
273 assert(ref != nullptr, "sanity");
274 level = trace_to_root(st, info->orig_referrer(), orig_obj, ref) + 1;
275 } else if (java_lang_String::is_instance(orig_obj)) {
276 st->print_cr("[%2d] (shared string table)", level++);
277 }
278 Klass* k = orig_obj->klass();
279 ResourceMark rm;
280 st->print("[%2d] ", level);
281 orig_obj->print_address_on(st);
282 st->print(" %s", k->internal_name());
283 if (orig_field != nullptr) {
284 if (k->is_instance_klass()) {
285 TraceFields clo(orig_obj, orig_field, st);
286 InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
287 } else {
288 assert(orig_obj->is_objArray(), "must be");
289 objArrayOop array = (objArrayOop)orig_obj;
290 for (int i = 0; i < array->length(); i++) {
291 if (array->obj_at(i) == orig_field) {
292 st->print(" @[%d]", i);
293 break;
294 }
295 }
296 }
297 }
298 st->cr();
299
300 return level;
301 }
302
|
1 /*
2 * Copyright (c) 2022, 2024, 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 #include "precompiled.hpp"
26 #include "cds/aotClassInitializer.hpp"
27 #include "cds/archiveBuilder.hpp"
28 #include "cds/cdsHeapVerifier.hpp"
29 #include "classfile/classLoaderDataGraph.hpp"
30 #include "classfile/javaClasses.inline.hpp"
31 #include "logging/log.hpp"
32 #include "logging/logStream.hpp"
33 #include "memory/resourceArea.hpp"
34 #include "oops/fieldStreams.inline.hpp"
35 #include "oops/klass.inline.hpp"
36 #include "oops/oop.inline.hpp"
37 #include "runtime/fieldDescriptor.inline.hpp"
38
39 #if INCLUDE_CDS_JAVA_HEAP
40
41 // CDSHeapVerifier is used to check for problems where an archived object references a
42 // static field that may be reinitialized at runtime. In the following example,
43 // Foo.get.test()
44 // correctly returns true when CDS disabled, but incorrectly returns false when CDS is enabled.
45 //
46 // class Foo {
107 // tested for equality.
108 ADD_EXCL("java/util/HashSet", "PRESENT"); // E
109 ADD_EXCL("jdk/internal/loader/BuiltinClassLoader", "packageToModule"); // A
110 ADD_EXCL("jdk/internal/loader/ClassLoaders", "BOOT_LOADER", // A
111 "APP_LOADER", // A
112 "PLATFORM_LOADER"); // A
113 ADD_EXCL("jdk/internal/module/Builder", "cachedVersion"); // D
114 ADD_EXCL("jdk/internal/module/ModuleLoaderMap$Mapper", "APP_CLASSLOADER", // A
115 "APP_LOADER_INDEX", // A
116 "PLATFORM_CLASSLOADER", // A
117 "PLATFORM_LOADER_INDEX"); // A
118 ADD_EXCL("jdk/internal/module/ServicesCatalog", "CLV"); // A
119
120 // This just points to an empty Map
121 ADD_EXCL("jdk/internal/reflect/Reflection", "methodFilterMap"); // E
122
123 // Integer for 0 and 1 are in java/lang/Integer$IntegerCache and are archived
124 ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E
125 "ZERO_INT"); // E
126
127 if (CDSConfig::is_dumping_invokedynamic()) {
128 ADD_EXCL("java/lang/invoke/MethodHandles", "IMPL_NAMES"); // D
129 ADD_EXCL("java/lang/invoke/MemberName$Factory", "INSTANCE"); // D
130 ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "MEMBERNAME_FACTORY"); // D
131 ADD_EXCL("java/lang/invoke/SimpleMethodHandle", "BMH_SPECIES"); // D
132 }
133
134 // These are used by BuiltinClassLoader::negativeLookupCache, etc but seem to be
135 // OK. TODO - we should copletely disable the caching unless ArchiveLoaderLookupCache
136 // is enabled
137 ADD_EXCL("java/lang/Boolean", "TRUE", // E
138 "FALSE"); // E
139
140 ADD_EXCL("java/lang/Boolean$AOTHolder", "TRUE", // E
141 "FALSE"); // E
142
143
144 # undef ADD_EXCL
145
146 ClassLoaderDataGraph::classes_do(this);
147 }
148
149 CDSHeapVerifier::~CDSHeapVerifier() {
150 if (_problems > 0) {
151 log_warning(cds, heap)("Scanned %d objects. Found %d case(s) where "
152 "an object points to a static field that may be "
153 "reinitialized at runtime.", _archived_objs, _problems);
154 }
155 }
156
157 class CDSHeapVerifier::CheckStaticFields : public FieldClosure {
158 CDSHeapVerifier* _verifier;
159 InstanceKlass* _ik; // The class whose static fields are being checked.
160 const char** _exclusions;
161 public:
162 CheckStaticFields(CDSHeapVerifier* verifier, InstanceKlass* ik)
163 : _verifier(verifier), _ik(ik) {
164 _exclusions = _verifier->find_exclusion(_ik);
165 }
166
167 void do_field(fieldDescriptor* fd) {
168 if (fd->field_type() != T_OBJECT) {
169 return;
170 }
171
172 oop static_obj_field = _ik->java_mirror()->obj_field(fd->offset());
173 if (static_obj_field != nullptr) {
174 Klass* klass_of_field = static_obj_field->klass();
175 if (_exclusions != nullptr) {
176 for (const char** p = _exclusions; *p != nullptr; p++) {
177 if (fd->name()->equals(*p)) {
178 return;
179 }
180 }
181 }
182
183 if (fd->is_final() && java_lang_String::is_instance(static_obj_field) && fd->has_initial_value()) {
184 // This field looks like like this in the Java source:
185 // static final SOME_STRING = "a string literal";
186 // This string literal has been stored in the shared string table, so it's OK
187 // for the archived objects to refer to it.
188 return;
189 }
190 if (fd->is_final() && java_lang_Class::is_instance(static_obj_field)) {
191 // This field points to an archived mirror.
192 return;
193 }
194 if (klass_of_field->has_archived_enum_objs()) {
195 // This field is an Enum. If any instance of this Enum has been archived, we will archive
196 // all static fields of this Enum as well.
197 // See HeapShared::initialize_enum_klass().
198 return;
199 }
200 if (klass_of_field->is_instance_klass()) {
201 if (InstanceKlass::cast(klass_of_field)->is_initialized() &&
202 AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass::cast(klass_of_field))) {
203 return;
204 }
205 }
206
207 if (AOTClassInitializer::can_archive_initialized_mirror(_ik)) {
208 return;
209 }
210
211 // This field *may* be initialized to a different value at runtime. Remember it
212 // and check later if it appears in the archived object graph.
213 _verifier->add_static_obj_field(_ik, static_obj_field, fd->name());
214 }
215 }
216 };
217
218 // Remember all the static object fields of every class that are currently
219 // loaded.
220 void CDSHeapVerifier::do_klass(Klass* k) {
221 if (k->is_instance_klass()) {
222 InstanceKlass* ik = InstanceKlass::cast(k);
223
224 if (HeapShared::is_subgraph_root_class(ik)) {
225 // ik is inside one of the ArchivableStaticFieldInfo tables
226 // in heapShared.cpp. We assume such classes are programmed to
227 // update their static fields correctly at runtime.
228 return;
229 }
230
231 if (HeapShared::is_lambda_form_klass(ik)) {
232 // Archived lambda forms have preinitialized mirrors, so <clinit> won't run.
233 return;
234 }
235
236 CheckStaticFields csf(this, ik);
237 ik->do_local_static_fields(&csf);
238 }
239 }
240
241 void CDSHeapVerifier::add_static_obj_field(InstanceKlass* ik, oop field, Symbol* name) {
242 if (field->klass() == vmClasses::MethodType_klass()) {
243 // The identity of MethodTypes are preserved between assembly phase and production runs
244 // (by MethodType::AOTHolder::archivedMethodTypes). No need to check.
245 return;
246 }
247 if (field->klass() == vmClasses::LambdaForm_klass()) {
248 // LambdaForms are non-modifiable and are not tested for object equality, so
249 // it's OK if static fields of the LambdaForm type are reinitialized at runtime with
250 // alternative instances. No need to check.
251 return;
252 }
253
254 StaticFieldInfo info = {ik, name};
255 _table.put(field, info);
256 }
257
258 inline bool CDSHeapVerifier::do_entry(oop& orig_obj, HeapShared::CachedOopInfo& value) {
259 _archived_objs++;
260
261 StaticFieldInfo* info = _table.get(orig_obj);
262 if (info != nullptr) {
263 if (value.orig_referrer() == nullptr && java_lang_String::is_instance(orig_obj)) {
264 // This string object is not referenced by any of the archived object graphs. It's archived
265 // only because it's in the interned string table. So we are not in a condition that
266 // should be flagged by CDSHeapVerifier.
267 return true; /* keep on iterating */
268 }
269 if (info->_holder->is_hidden()) {
270 return true;
271 }
272 ResourceMark rm;
273 char* class_name = info->_holder->name()->as_C_string();
274 char* field_name = info->_name->as_C_string();
275 if (strstr(class_name, "java/lang/invoke/BoundMethodHandle$Species_") == class_name &&
276 strcmp(field_name, "BMH_SPECIES") == 0) {
277 return true;
278 }
279 LogStream ls(Log(cds, heap)::warning());
280 ls.print_cr("Archive heap points to a static field that may be reinitialized at runtime:");
281 ls.print_cr("Field: %s::%s", class_name, field_name);
282 ls.print("Value: ");
283 orig_obj->print_on(&ls);
284 ls.print_cr("--- trace begin ---");
285 trace_to_root(&ls, orig_obj, nullptr, &value);
286 ls.print_cr("--- trace end ---");
287 ls.cr();
288 _problems ++;
289 }
290
291 return true; /* keep on iterating */
292 }
293
294 class CDSHeapVerifier::TraceFields : public FieldClosure {
295 oop _orig_obj;
296 oop _orig_field;
297 outputStream* _st;
298
299 public:
300 TraceFields(oop orig_obj, oop orig_field, outputStream* st)
301 : _orig_obj(orig_obj), _orig_field(orig_field), _st(st) {}
317 trace_to_root(st, orig_obj, nullptr, info);
318 } else {
319 st->print_cr("Not an archived object??");
320 }
321 }
322
323 int CDSHeapVerifier::trace_to_root(outputStream* st, oop orig_obj, oop orig_field, HeapShared::CachedOopInfo* info) {
324 int level = 0;
325 if (info->orig_referrer() != nullptr) {
326 HeapShared::CachedOopInfo* ref = HeapShared::archived_object_cache()->get(info->orig_referrer());
327 assert(ref != nullptr, "sanity");
328 level = trace_to_root(st, info->orig_referrer(), orig_obj, ref) + 1;
329 } else if (java_lang_String::is_instance(orig_obj)) {
330 st->print_cr("[%2d] (shared string table)", level++);
331 }
332 Klass* k = orig_obj->klass();
333 ResourceMark rm;
334 st->print("[%2d] ", level);
335 orig_obj->print_address_on(st);
336 st->print(" %s", k->internal_name());
337 if (java_lang_Class::is_instance(orig_obj)) {
338 st->print(" (%s)", java_lang_Class::as_Klass(orig_obj)->external_name());
339 }
340 if (orig_field != nullptr) {
341 if (k->is_instance_klass()) {
342 TraceFields clo(orig_obj, orig_field, st);
343 InstanceKlass::cast(k)->do_nonstatic_fields(&clo);
344 } else {
345 assert(orig_obj->is_objArray(), "must be");
346 objArrayOop array = (objArrayOop)orig_obj;
347 for (int i = 0; i < array->length(); i++) {
348 if (array->obj_at(i) == orig_field) {
349 st->print(" @[%d]", i);
350 break;
351 }
352 }
353 }
354 }
355 st->cr();
356
357 return level;
358 }
359
|