1 /*
2 * Copyright (c) 2021, 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 #include "cds/aotClassLocation.hpp"
26 #include "cds/cdsConfig.hpp"
27 #include "cds/cdsProtectionDomain.hpp"
28 #include "classfile/classLoader.hpp"
29 #include "classfile/classLoaderData.inline.hpp"
30 #include "classfile/javaClasses.hpp"
31 #include "classfile/moduleEntry.hpp"
32 #include "classfile/systemDictionaryShared.hpp"
33 #include "classfile/vmClasses.hpp"
34 #include "classfile/vmSymbols.hpp"
35 #include "memory/oopFactory.hpp"
36 #include "memory/resourceArea.hpp"
37 #include "memory/universe.hpp"
38 #include "oops/instanceKlass.hpp"
39 #include "oops/oopCast.inline.hpp"
40 #include "oops/refArrayOop.hpp"
41 #include "oops/symbol.hpp"
42 #include "runtime/javaCalls.hpp"
43
44 OopHandle CDSProtectionDomain::_shared_protection_domains;
45 OopHandle CDSProtectionDomain::_shared_jar_urls;
46 OopHandle CDSProtectionDomain::_shared_jar_manifests;
47
48 // Initializes the java.lang.Package and java.security.ProtectionDomain objects associated with
49 // the given InstanceKlass.
50 // Returns the ProtectionDomain for the InstanceKlass.
51 Handle CDSProtectionDomain::init_security_info(Handle class_loader, InstanceKlass* ik, PackageEntry* pkg_entry, TRAPS) {
52 int index = ik->shared_classpath_index();
53 assert(index >= 0, "Sanity");
54 const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(index);
55 Symbol* class_name = ik->name();
56
57 if (cl->is_modules_image()) {
58 // For shared app/platform classes originated from the run-time image:
59 // The ProtectionDomains are cached in the corresponding ModuleEntries
60 // for fast access by the VM.
61 // all packages from module image are already created during VM bootstrap in
62 // Modules::define_module().
63 assert(pkg_entry != nullptr, "archived class in module image cannot be from unnamed package");
64 ModuleEntry* mod_entry = pkg_entry->module();
65 return get_shared_protection_domain(class_loader, mod_entry, THREAD);
66 } else {
67 // For shared app/platform classes originated from JAR files on the class path:
68 // Each of the 3 CDSProtectionDomain::_shared_xxx arrays has the same length
69 // as the shared classpath table in the shared archive.
70 //
71 // If a shared InstanceKlass k is loaded from the class path, let
72 //
73 // index = k->shared_classpath_index();
74 //
75 // AOTClassLocationConfig::_runtime_instance->_array->at(index) identifies the JAR file that contains k.
76 //
77 // k's protection domain is:
78 //
79 // ProtectionDomain pd = _shared_protection_domains[index];
80 //
81 // and k's Package is initialized using
82 //
83 // manifest = _shared_jar_manifests[index];
84 // url = _shared_jar_urls[index];
85 // define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
86 //
87 // Note that if an element of these 3 _shared_xxx arrays is null, it will be initialized by
88 // the corresponding CDSProtectionDomain::get_shared_xxx() function.
89 Handle manifest = get_shared_jar_manifest(index, CHECK_NH);
90 Handle url = get_shared_jar_url(index, CHECK_NH);
91 int index_offset = index - AOTClassLocationConfig::runtime()->app_cp_start_index();
92 if (index_offset < PackageEntry::max_index_for_defined_in_class_path()) {
93 if (pkg_entry == nullptr || !pkg_entry->is_defined_by_cds_in_class_path(index_offset)) {
94 // define_shared_package only needs to be called once for each package in a jar specified
95 // in the shared class path.
96 define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
97 if (pkg_entry != nullptr) {
98 pkg_entry->set_defined_by_cds_in_class_path(index_offset);
99 }
100 }
101 } else {
102 define_shared_package(class_name, class_loader, manifest, url, CHECK_NH);
103 }
104 return get_shared_protection_domain(class_loader, index, url, THREAD);
105 }
106 }
107
108 Handle CDSProtectionDomain::get_package_name(Symbol* class_name, TRAPS) {
109 ResourceMark rm(THREAD);
110 Handle pkgname_string;
111 TempNewSymbol pkg = ClassLoader::package_from_class_name(class_name);
112 if (pkg != nullptr) { // Package prefix found
113 const char* pkgname = pkg->as_klass_external_name();
114 pkgname_string = java_lang_String::create_from_str(pkgname,
115 CHECK_(pkgname_string));
116 }
117 return pkgname_string;
118 }
119
120 PackageEntry* CDSProtectionDomain::get_package_entry_from_class(InstanceKlass* ik, Handle class_loader) {
121 PackageEntry* pkg_entry = ik->package();
122 if (CDSConfig::is_using_full_module_graph() && ik->in_aot_cache() && pkg_entry != nullptr) {
123 assert(AOTMetaspace::in_aot_cache(pkg_entry), "must be");
124 assert(!ik->defined_by_other_loaders(), "unexpected archived package entry for an unregistered class");
125 return pkg_entry;
126 }
127 TempNewSymbol pkg_name = ClassLoader::package_from_class_name(ik->name());
128 if (pkg_name != nullptr) {
129 pkg_entry = ClassLoaderData::class_loader_data(class_loader())->packages()->lookup_only(pkg_name);
130 } else {
131 pkg_entry = nullptr;
132 }
133 return pkg_entry;
134 }
135
136 // Define Package for shared app classes from JAR file and also checks for
137 // package sealing (all done in Java code)
138 // See http://docs.oracle.com/javase/tutorial/deployment/jar/sealman.html
139 void CDSProtectionDomain::define_shared_package(Symbol* class_name,
140 Handle class_loader,
141 Handle manifest,
142 Handle url,
143 TRAPS) {
144 assert(SystemDictionary::is_system_class_loader(class_loader()), "unexpected class loader");
145 // get_package_name() returns a null handle if the class is in unnamed package
146 Handle pkgname_string = get_package_name(class_name, CHECK);
147 if (pkgname_string.not_null()) {
148 Klass* app_classLoader_klass = vmClasses::jdk_internal_loader_ClassLoaders_AppClassLoader_klass();
149 JavaValue result(T_OBJECT);
150 JavaCallArguments args(3);
151 args.set_receiver(class_loader);
152 args.push_oop(pkgname_string);
153 args.push_oop(manifest);
154 args.push_oop(url);
155 JavaCalls::call_virtual(&result, app_classLoader_klass,
156 vmSymbols::defineOrCheckPackage_name(),
157 vmSymbols::defineOrCheckPackage_signature(),
158 &args,
159 CHECK);
160 }
161 }
162
163 Handle CDSProtectionDomain::create_jar_manifest(const char* manifest_chars, size_t size, TRAPS) {
164 typeArrayOop buf = oopFactory::new_byteArray((int)size, CHECK_NH);
165 typeArrayHandle bufhandle(THREAD, buf);
166 ArrayAccess<>::arraycopy_from_native(reinterpret_cast<const jbyte*>(manifest_chars),
167 buf, typeArrayOopDesc::element_offset<jbyte>(0), size);
168 Handle bais = JavaCalls::construct_new_instance(vmClasses::ByteArrayInputStream_klass(),
169 vmSymbols::byte_array_void_signature(),
170 bufhandle, CHECK_NH);
171 // manifest = new Manifest(ByteArrayInputStream)
172 Handle manifest = JavaCalls::construct_new_instance(vmClasses::Jar_Manifest_klass(),
173 vmSymbols::input_stream_void_signature(),
174 bais, CHECK_NH);
175 return manifest;
176 }
177
178 Handle CDSProtectionDomain::get_shared_jar_manifest(int shared_path_index, TRAPS) {
179 Handle manifest;
180 if (shared_jar_manifest(shared_path_index) == nullptr) {
181 const AOTClassLocation* cl = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index);
182 size_t size = cl->manifest_length();
183 if (size == 0) {
184 return Handle();
185 }
186
187 // ByteArrayInputStream bais = new ByteArrayInputStream(buf);
188 const char* src = cl->manifest();
189 assert(src != nullptr, "No Manifest data");
190 manifest = create_jar_manifest(src, size, CHECK_NH);
191 atomic_set_shared_jar_manifest(shared_path_index, manifest());
192 }
193 manifest = Handle(THREAD, shared_jar_manifest(shared_path_index));
194 assert(manifest.not_null(), "sanity");
195 return manifest;
196 }
197
198 Handle CDSProtectionDomain::get_shared_jar_url(int shared_path_index, TRAPS) {
199 Handle url_h;
200 if (shared_jar_url(shared_path_index) == nullptr) {
201 const char* path = AOTClassLocationConfig::runtime()->class_location_at(shared_path_index)->path();
202 oop result_oop = to_file_URL(path, url_h, CHECK_(url_h));
203 atomic_set_shared_jar_url(shared_path_index, result_oop);
204 }
205
206 url_h = Handle(THREAD, shared_jar_url(shared_path_index));
207 assert(url_h.not_null(), "sanity");
208 return url_h;
209 }
210
211 oop CDSProtectionDomain::to_file_URL(const char* path, Handle url_h, TRAPS) {
212 JavaValue result(T_OBJECT);
213 Handle path_string = java_lang_String::create_from_str(path, CHECK_NULL);
214 JavaCalls::call_static(&result,
215 vmClasses::jdk_internal_loader_ClassLoaders_klass(),
216 vmSymbols::toFileURL_name(),
217 vmSymbols::toFileURL_signature(),
218 path_string, CHECK_NULL);
219 return result.get_oop();
220 }
221
222 // Get the ProtectionDomain associated with the CodeSource from the classloader.
223 Handle CDSProtectionDomain::get_protection_domain_from_classloader(Handle class_loader,
224 Handle url, TRAPS) {
225 // CodeSource cs = new CodeSource(url, null);
226 Handle cs = JavaCalls::construct_new_instance(vmClasses::CodeSource_klass(),
227 vmSymbols::url_code_signer_array_void_signature(),
228 url, Handle(), CHECK_NH);
229
230 // protection_domain = SecureClassLoader.getProtectionDomain(cs);
231 Klass* secureClassLoader_klass = vmClasses::SecureClassLoader_klass();
232 JavaValue obj_result(T_OBJECT);
233 JavaCalls::call_virtual(&obj_result, class_loader, secureClassLoader_klass,
234 vmSymbols::getProtectionDomain_name(),
235 vmSymbols::getProtectionDomain_signature(),
236 cs, CHECK_NH);
237 return Handle(THREAD, obj_result.get_oop());
238 }
239
240 // Returns the ProtectionDomain associated with the JAR file identified by the url.
241 Handle CDSProtectionDomain::get_shared_protection_domain(Handle class_loader,
242 int shared_path_index,
243 Handle url,
244 TRAPS) {
245 Handle protection_domain;
246 if (shared_protection_domain(shared_path_index) == nullptr) {
247 Handle pd = get_protection_domain_from_classloader(class_loader, url, CHECK_NH);
248 atomic_set_shared_protection_domain(shared_path_index, pd());
249 }
250
251 // Acquire from the cache because if another thread beats the current one to
252 // set the shared protection_domain and the atomic_set fails, the current thread
253 // needs to get the updated protection_domain from the cache.
254 protection_domain = Handle(THREAD, shared_protection_domain(shared_path_index));
255 assert(protection_domain.not_null(), "sanity");
256 return protection_domain;
257 }
258
259 // Returns the ProtectionDomain associated with the moduleEntry.
260 Handle CDSProtectionDomain::get_shared_protection_domain(Handle class_loader,
261 ModuleEntry* mod, TRAPS) {
262 ClassLoaderData *loader_data = mod->loader_data();
263 if (mod->shared_protection_domain() == nullptr) {
264 Symbol* location = mod->location();
265 if (location != nullptr) {
266 Handle location_string = java_lang_String::create_from_symbol(
267 location, CHECK_NH);
268 Handle url;
269 JavaValue result(T_OBJECT);
270 if (location->starts_with("jrt:/")) {
271 url = JavaCalls::construct_new_instance(vmClasses::URL_klass(),
272 vmSymbols::string_void_signature(),
273 location_string, CHECK_NH);
274 } else {
275 Klass* classLoaders_klass =
276 vmClasses::jdk_internal_loader_ClassLoaders_klass();
277 JavaCalls::call_static(&result, classLoaders_klass, vmSymbols::toFileURL_name(),
278 vmSymbols::toFileURL_signature(),
279 location_string, CHECK_NH);
280 url = Handle(THREAD, result.get_oop());
281 }
282
283 Handle pd = get_protection_domain_from_classloader(class_loader, url,
284 CHECK_NH);
285 mod->set_shared_protection_domain(loader_data, pd);
286 }
287 }
288
289 Handle protection_domain(THREAD, mod->shared_protection_domain());
290 assert(protection_domain.not_null(), "sanity");
291 return protection_domain;
292 }
293
294 void CDSProtectionDomain::atomic_set_array_index(OopHandle array, int index, oop o) {
295 // Benign race condition: array.obj_at(index) may already be filled in.
296 // The important thing here is that all threads pick up the same result.
297 // It doesn't matter which racing thread wins, as long as only one
298 // result is used by all threads, and all future queries.
299 oop_cast<refArrayOop>(array.resolve())->replace_if_null(index, o);
300 }
301
302 oop CDSProtectionDomain::shared_protection_domain(int index) {
303 return oop_cast<refArrayOop>(_shared_protection_domains.resolve())->obj_at(index);
304 }
305
306 void CDSProtectionDomain::allocate_shared_protection_domain_array(int size, TRAPS) {
307 if (_shared_protection_domains.resolve() == nullptr) {
308 oop spd = oopFactory::new_refArray(vmClasses::ProtectionDomain_klass(), size, CHECK);
309 _shared_protection_domains = OopHandle(Universe::vm_global(), spd);
310 }
311 }
312
313 oop CDSProtectionDomain::shared_jar_url(int index) {
314 return oop_cast<refArrayOop>(_shared_jar_urls.resolve())->obj_at(index);
315 }
316
317 void CDSProtectionDomain::allocate_shared_jar_url_array(int size, TRAPS) {
318 if (_shared_jar_urls.resolve() == nullptr) {
319 oop sju = oopFactory::new_refArray(vmClasses::URL_klass(), size, CHECK);
320 _shared_jar_urls = OopHandle(Universe::vm_global(), sju);
321 }
322 }
323
324 oop CDSProtectionDomain::shared_jar_manifest(int index) {
325 return oop_cast<refArrayOop>(_shared_jar_manifests.resolve())->obj_at(index);
326 }
327
328 void CDSProtectionDomain::allocate_shared_jar_manifest_array(int size, TRAPS) {
329 if (_shared_jar_manifests.resolve() == nullptr) {
330 oop sjm = oopFactory::new_refArray(vmClasses::Jar_Manifest_klass(), size, CHECK);
331 _shared_jar_manifests = OopHandle(Universe::vm_global(), sjm);
332 }
333 }