blob: 5c1677fea3440f1b2fda00901a4de5635c6299ea [file] [log] [blame]
Andreas Gampece7732b2017-01-17 15:50:26 -08001/* Copyright (C) 2017 The Android Open Source Project
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3 *
4 * This file implements interfaces from the file jvmti.h. This implementation
5 * is licensed under the same terms as the file jvmti.h. The
6 * copyright and license information for the file jvmti.h follows.
7 *
8 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10 *
11 * This code is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License version 2 only, as
13 * published by the Free Software Foundation. Oracle designates this
14 * particular file as subject to the "Classpath" exception as provided
15 * by Oracle in the LICENSE file that accompanied this code.
16 *
17 * This code is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * version 2 for more details (a copy is included in the LICENSE file that
21 * accompanied this code).
22 *
23 * You should have received a copy of the GNU General Public License version
24 * 2 along with this work; if not, write to the Free Software Foundation,
25 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 *
27 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28 * or visit www.oracle.com if you need additional information or have any
29 * questions.
30 */
31
Alex Light639e73b2019-05-17 21:44:36 +000032#include <sstream>
33#include <unistd.h>
34
Andreas Gampece7732b2017-01-17 15:50:26 -080035#include "ti_search.h"
36
37#include "jni.h"
38
Andreas Gampea1d2f952017-04-20 22:53:58 -070039#include "art_field-inl.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080040#include "art_jvmti.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080041#include "base/enums.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080042#include "base/macros.h"
Alex Light639e73b2019-05-17 21:44:36 +000043#include "base/memfd.h"
44#include "base/os.h"
45#include "base/unix_file/fd_file.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080046#include "class_linker.h"
David Sehr013fd802018-01-11 22:55:24 -080047#include "dex/art_dex_file_loader.h"
David Sehr9e734c72018-01-04 17:56:19 -080048#include "dex/dex_file.h"
49#include "dex/dex_file_loader.h"
Vladimir Markoa3ad0cd2018-05-04 10:06:38 +010050#include "jni/jni_internal.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080051#include "mirror/class-inl.h"
52#include "mirror/object.h"
53#include "mirror/string.h"
Andreas Gampe373a9b52017-10-18 09:01:57 -070054#include "nativehelper/scoped_local_ref.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080055#include "obj_ptr-inl.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080056#include "runtime.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080057#include "runtime_callbacks.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080058#include "scoped_thread_state_change-inl.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070059#include "thread-current-inl.h"
Andreas Gampecefaa142017-01-23 15:04:59 -080060#include "thread_list.h"
Alex Lightae45cbb2018-10-18 15:49:56 -070061#include "ti_logging.h"
Steven Morelande431e272017-07-18 16:53:49 -070062#include "ti_phase.h"
Andreas Gampea1d2f952017-04-20 22:53:58 -070063#include "well_known_classes.h"
Andreas Gampece7732b2017-01-17 15:50:26 -080064
65namespace openjdkjvmti {
66
Andreas Gampecefaa142017-01-23 15:04:59 -080067static std::vector<std::string> gSystemOnloadSegments;
68
69static art::ObjPtr<art::mirror::Object> GetSystemProperties(art::Thread* self,
70 art::ClassLinker* class_linker)
71 REQUIRES_SHARED(art::Locks::mutator_lock_) {
72 art::ObjPtr<art::mirror::Class> system_class =
73 class_linker->LookupClass(self, "Ljava/lang/System;", nullptr);
74 DCHECK(system_class != nullptr);
75 DCHECK(system_class->IsInitialized());
76
77 art::ArtField* props_field =
78 system_class->FindDeclaredStaticField("props", "Ljava/util/Properties;");
79 DCHECK(props_field != nullptr);
80
81 art::ObjPtr<art::mirror::Object> props_obj = props_field->GetObject(system_class);
82 DCHECK(props_obj != nullptr);
83
84 return props_obj;
85}
86
87static void Update() REQUIRES_SHARED(art::Locks::mutator_lock_) {
88 if (gSystemOnloadSegments.empty()) {
89 return;
90 }
91
92 // In the on-load phase we have to modify java.class.path to influence the system classloader.
93 // As this is an unmodifiable system property, we have to access the "defaults" field.
94 art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
95 DCHECK(class_linker != nullptr);
96 art::Thread* self = art::Thread::Current();
97
98 // Prepare: collect classes, fields and methods.
99 art::ObjPtr<art::mirror::Class> properties_class =
100 class_linker->LookupClass(self, "Ljava/util/Properties;", nullptr);
101 DCHECK(properties_class != nullptr);
102
103 ScopedLocalRef<jobject> defaults_jobj(self->GetJniEnv(), nullptr);
104 {
105 art::ObjPtr<art::mirror::Object> props_obj = GetSystemProperties(self, class_linker);
106
107 art::ArtField* defaults_field =
108 properties_class->FindDeclaredInstanceField("defaults", "Ljava/util/Properties;");
109 DCHECK(defaults_field != nullptr);
110
111 art::ObjPtr<art::mirror::Object> defaults_obj = defaults_field->GetObject(props_obj);
112 DCHECK(defaults_obj != nullptr);
113 defaults_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(defaults_obj));
114 }
115
116 art::ArtMethod* get_property =
Vladimir Markoba118822017-06-12 15:41:56 +0100117 properties_class->FindClassMethod(
Andreas Gampecefaa142017-01-23 15:04:59 -0800118 "getProperty",
119 "(Ljava/lang/String;)Ljava/lang/String;",
120 art::kRuntimePointerSize);
121 DCHECK(get_property != nullptr);
Vladimir Markoba118822017-06-12 15:41:56 +0100122 DCHECK(!get_property->IsDirect());
123 DCHECK(get_property->GetDeclaringClass() == properties_class);
Andreas Gampecefaa142017-01-23 15:04:59 -0800124 art::ArtMethod* set_property =
Vladimir Markoba118822017-06-12 15:41:56 +0100125 properties_class->FindClassMethod(
Andreas Gampecefaa142017-01-23 15:04:59 -0800126 "setProperty",
127 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
128 art::kRuntimePointerSize);
129 DCHECK(set_property != nullptr);
Vladimir Markoba118822017-06-12 15:41:56 +0100130 DCHECK(!set_property->IsDirect());
131 DCHECK(set_property->GetDeclaringClass() == properties_class);
Andreas Gampecefaa142017-01-23 15:04:59 -0800132
133 // This is an allocation. Do this late to avoid the need for handles.
134 ScopedLocalRef<jobject> cp_jobj(self->GetJniEnv(), nullptr);
135 {
136 art::ObjPtr<art::mirror::Object> cp_key =
137 art::mirror::String::AllocFromModifiedUtf8(self, "java.class.path");
138 if (cp_key == nullptr) {
139 self->AssertPendingOOMException();
140 self->ClearException();
141 return;
142 }
143 cp_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(cp_key));
144 }
145
146 // OK, now get the current value.
147 std::string str_value;
148 {
149 ScopedLocalRef<jobject> old_value(self->GetJniEnv(),
150 self->GetJniEnv()->CallObjectMethod(
151 defaults_jobj.get(),
152 art::jni::EncodeArtMethod(get_property),
153 cp_jobj.get()));
154 DCHECK(old_value.get() != nullptr);
155
156 str_value = self->DecodeJObject(old_value.get())->AsString()->ToModifiedUtf8();
157 self->GetJniEnv()->DeleteLocalRef(old_value.release());
158 }
159
160 // Update the value by appending the new segments.
161 for (const std::string& segment : gSystemOnloadSegments) {
162 if (!str_value.empty()) {
163 str_value += ":";
164 }
165 str_value += segment;
166 }
167 gSystemOnloadSegments.clear();
168
169 // Create the new value object.
170 ScopedLocalRef<jobject> new_val_jobj(self->GetJniEnv(), nullptr);
171 {
172 art::ObjPtr<art::mirror::Object> new_value =
173 art::mirror::String::AllocFromModifiedUtf8(self, str_value.c_str());
174 if (new_value == nullptr) {
175 self->AssertPendingOOMException();
176 self->ClearException();
177 return;
178 }
179
180 new_val_jobj.reset(self->GetJniEnv()->AddLocalReference<jobject>(new_value));
181 }
182
183 // Write to the defaults.
184 ScopedLocalRef<jobject> res_obj(self->GetJniEnv(),
185 self->GetJniEnv()->CallObjectMethod(defaults_jobj.get(),
186 art::jni::EncodeArtMethod(set_property),
187 cp_jobj.get(),
188 new_val_jobj.get()));
189 if (self->IsExceptionPending()) {
190 self->ClearException();
191 return;
192 }
193}
194
195struct SearchCallback : public art::RuntimePhaseCallback {
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100196 void NextRuntimePhase(RuntimePhase phase) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
Andreas Gampecefaa142017-01-23 15:04:59 -0800197 if (phase == RuntimePhase::kStart) {
198 // It's time to update the system properties.
199 Update();
200 }
201 }
202};
203
204static SearchCallback gSearchCallback;
205
206void SearchUtil::Register() {
207 art::Runtime* runtime = art::Runtime::Current();
208
209 art::ScopedThreadStateChange stsc(art::Thread::Current(),
210 art::ThreadState::kWaitingForDebuggerToAttach);
211 art::ScopedSuspendAll ssa("Add search callback");
212 runtime->GetRuntimeCallbacks()->AddRuntimePhaseCallback(&gSearchCallback);
213}
214
215void SearchUtil::Unregister() {
216 art::ScopedThreadStateChange stsc(art::Thread::Current(),
217 art::ThreadState::kWaitingForDebuggerToAttach);
218 art::ScopedSuspendAll ssa("Remove search callback");
219 art::Runtime* runtime = art::Runtime::Current();
220 runtime->GetRuntimeCallbacks()->RemoveRuntimePhaseCallback(&gSearchCallback);
221}
222
Alex Lightae45cbb2018-10-18 15:49:56 -0700223jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env,
Andreas Gampece7732b2017-01-17 15:50:26 -0800224 const char* segment) {
225 art::Runtime* current = art::Runtime::Current();
226 if (current == nullptr) {
227 return ERR(WRONG_PHASE);
228 }
229 if (current->GetClassLinker() == nullptr) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800230 return ERR(WRONG_PHASE);
231 }
232 if (segment == nullptr) {
233 return ERR(NULL_POINTER);
234 }
235
236 std::string error_msg;
237 std::vector<std::unique_ptr<const art::DexFile>> dex_files;
David Sehr013fd802018-01-11 22:55:24 -0800238 const art::ArtDexFileLoader dex_file_loader;
Andreas Gampe6e897762018-10-16 13:09:32 -0700239 if (!dex_file_loader.Open(segment,
240 segment,
241 /* verify= */ true,
242 /* verify_checksum= */ true,
243 &error_msg,
244 &dex_files)) {
Alex Lightae45cbb2018-10-18 15:49:56 -0700245 JVMTI_LOG(WARNING, env) << "Could not open " << segment << " for boot classpath extension: "
246 << error_msg;
Andreas Gampece7732b2017-01-17 15:50:26 -0800247 return ERR(ILLEGAL_ARGUMENT);
248 }
249
Stefano Cianciulli94383b42022-09-02 10:54:44 +0000250 current->AddExtraBootDexFiles(segment, segment, std::move(dex_files));
251 return OK;
Andreas Gampece7732b2017-01-17 15:50:26 -0800252}
253
Alex Light639e73b2019-05-17 21:44:36 +0000254jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
255 jobject classloader,
256 const char* dex_bytes,
257 jint dex_bytes_length) {
258 if (jvmti_env == nullptr) {
259 return ERR(INVALID_ENVIRONMENT);
260 } else if (art::Thread::Current() == nullptr) {
261 return ERR(UNATTACHED_THREAD);
262 } else if (classloader == nullptr) {
263 return ERR(NULL_POINTER);
264 } else if (dex_bytes == nullptr) {
265 return ERR(NULL_POINTER);
266 } else if (dex_bytes_length <= 0) {
267 return ERR(ILLEGAL_ARGUMENT);
268 }
269
270 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
271
272 // TODO We really should try to support doing this during the ON_LOAD phase.
273 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
274 JVMTI_LOG(INFO, jvmti_env) << "Cannot add buffers to classpath during ON_LOAD phase to "
275 << "prevent file-descriptor leaking.";
276 return ERR(WRONG_PHASE);
277 }
278
279 // We have java APIs for adding files to the classpath, we might as well use them. It simplifies a
280 // lot of code as well.
281
282 // Create a memfd
283 art::File file(art::memfd_create_compat("JVMTI InMemory Added dex file", 0), /*check-usage*/true);
284 if (file.Fd() < 0) {
285 char* reason = strerror(errno);
286 JVMTI_LOG(ERROR, jvmti_env) << "Unable to create memfd due to " << reason;
Alex Lightbee28942019-05-30 10:03:25 -0700287 if (file.FlushClose() < 0) {
288 PLOG(WARNING) << "Failed to close file!";
289 }
Alex Light639e73b2019-05-17 21:44:36 +0000290 return ERR(INTERNAL);
291 }
292 // Fill it with the buffer.
293 if (!file.WriteFully(dex_bytes, dex_bytes_length) || file.Flush() != 0) {
294 JVMTI_LOG(ERROR, jvmti_env) << "Failed to write to memfd!";
Alex Lightbee28942019-05-30 10:03:25 -0700295 if (file.FlushClose() < 0) {
296 PLOG(WARNING) << "Failed to close file!";
297 }
Alex Light639e73b2019-05-17 21:44:36 +0000298 return ERR(INTERNAL);
299 }
300 // Get the filename in procfs.
301 std::ostringstream oss;
302 oss << "/proc/self/fd/" << file.Fd();
303 std::string seg(oss.str());
304 // Use common code.
305
306 jvmtiError result = AddToDexClassLoader(jvmti_env, classloader, seg.c_str());
307 // We have either loaded the dex file and have a new MemMap pointing to the same pages or loading
308 // has failed and the memory isn't needed anymore. Either way we can close the memfd we created
309 // and return.
310 if (file.Close() != 0) {
311 JVMTI_LOG(WARNING, jvmti_env) << "Failed to close memfd!";
312 }
313 return result;
314}
315
316jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env,
317 jobject classloader,
318 const char* segment) {
319 if (jvmti_env == nullptr) {
320 return ERR(INVALID_ENVIRONMENT);
321 } else if (art::Thread::Current() == nullptr) {
322 return ERR(UNATTACHED_THREAD);
323 } else if (classloader == nullptr) {
324 return ERR(NULL_POINTER);
325 } else if (segment == nullptr) {
326 return ERR(NULL_POINTER);
327 }
328
329 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
330
331 // TODO We really should try to support doing this during the ON_LOAD phase.
332 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
333 JVMTI_LOG(INFO, jvmti_env) << "Cannot add to classpath of arbitrary classloaders during "
334 << "ON_LOAD phase.";
335 return ERR(WRONG_PHASE);
336 }
337
338 // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
339 // exceptions are swallowed.
340
341 art::Thread* self = art::Thread::Current();
342 JNIEnv* env = self->GetJniEnv();
343 if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
344 JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
345 return ERR(CLASS_LOADER_UNSUPPORTED);
346 }
347
348 jmethodID add_dex_path_id = env->GetMethodID(
349 art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
350 "addDexPath",
351 "(Ljava/lang/String;)V");
352 if (add_dex_path_id == nullptr) {
353 return ERR(INTERNAL);
354 }
355
356 ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
357 if (dex_path.get() == nullptr) {
358 return ERR(INTERNAL);
359 }
360 env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
361
362 if (env->ExceptionCheck()) {
363 {
364 art::ScopedObjectAccess soa(self);
365 JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
366 << self->GetException()->Dump();
367 }
368 env->ExceptionClear();
369 return ERR(ILLEGAL_ARGUMENT);
370 }
371 return OK;
372}
373
374jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const char* segment) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800375 if (segment == nullptr) {
376 return ERR(NULL_POINTER);
377 }
378
Andreas Gampecefaa142017-01-23 15:04:59 -0800379 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
380
381 if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) {
382 // We could try and see whether it is a valid path. We could also try to allocate Java
383 // objects to avoid later OOME.
384 gSystemOnloadSegments.push_back(segment);
385 return ERR(NONE);
386 } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800387 return ERR(WRONG_PHASE);
388 }
Andreas Gampecefaa142017-01-23 15:04:59 -0800389
Alex Light639e73b2019-05-17 21:44:36 +0000390 jobject loader = art::Runtime::Current()->GetSystemClassLoader();
391 if (loader == nullptr) {
Andreas Gampecefaa142017-01-23 15:04:59 -0800392 return ERR(INTERNAL);
Andreas Gampece7732b2017-01-17 15:50:26 -0800393 }
394
Andreas Gampece7732b2017-01-17 15:50:26 -0800395 art::Thread* self = art::Thread::Current();
396 JNIEnv* env = self->GetJniEnv();
Alex Light639e73b2019-05-17 21:44:36 +0000397 if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
Andreas Gampece7732b2017-01-17 15:50:26 -0800398 return ERR(INTERNAL);
399 }
400
Alex Light639e73b2019-05-17 21:44:36 +0000401 return AddToDexClassLoader(jvmti_env, loader, segment);
Andreas Gampece7732b2017-01-17 15:50:26 -0800402}
403
404} // namespace openjdkjvmti