1 /* 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
32 #include <sstream>
33 #include <unistd.h>
34
35 #include "ti_search.h"
36
37 #include "jni.h"
38
39 #include "art_field-inl.h"
40 #include "art_jvmti.h"
41 #include "base/enums.h"
42 #include "base/macros.h"
43 #include "base/memfd.h"
44 #include "base/os.h"
45 #include "base/unix_file/fd_file.h"
46 #include "class_linker.h"
47 #include "dex/art_dex_file_loader.h"
48 #include "dex/dex_file.h"
49 #include "dex/dex_file_loader.h"
50 #include "jni/jni_internal.h"
51 #include "mirror/class-inl.h"
52 #include "mirror/object.h"
53 #include "mirror/string.h"
54 #include "nativehelper/scoped_local_ref.h"
55 #include "obj_ptr-inl.h"
56 #include "runtime.h"
57 #include "runtime_callbacks.h"
58 #include "scoped_thread_state_change-inl.h"
59 #include "thread-current-inl.h"
60 #include "thread_list.h"
61 #include "ti_logging.h"
62 #include "ti_phase.h"
63 #include "well_known_classes.h"
64
65 namespace openjdkjvmti {
66
67 static std::vector<std::string> gSystemOnloadSegments;
68
GetSystemProperties(art::Thread * self,art::ClassLinker * class_linker)69 static 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
Update()87 static 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 =
117 properties_class->FindClassMethod(
118 "getProperty",
119 "(Ljava/lang/String;)Ljava/lang/String;",
120 art::kRuntimePointerSize);
121 DCHECK(get_property != nullptr);
122 DCHECK(!get_property->IsDirect());
123 DCHECK(get_property->GetDeclaringClass() == properties_class);
124 art::ArtMethod* set_property =
125 properties_class->FindClassMethod(
126 "setProperty",
127 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;",
128 art::kRuntimePointerSize);
129 DCHECK(set_property != nullptr);
130 DCHECK(!set_property->IsDirect());
131 DCHECK(set_property->GetDeclaringClass() == properties_class);
132
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
195 struct SearchCallback : public art::RuntimePhaseCallback {
NextRuntimePhaseopenjdkjvmti::SearchCallback196 void NextRuntimePhase(RuntimePhase phase) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
197 if (phase == RuntimePhase::kStart) {
198 // It's time to update the system properties.
199 Update();
200 }
201 }
202 };
203
204 static SearchCallback gSearchCallback;
205
Register()206 void 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
Unregister()215 void 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
AddToBootstrapClassLoaderSearch(jvmtiEnv * env,const char * segment)223 jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env,
224 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) {
230 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;
238 const art::ArtDexFileLoader dex_file_loader;
239 if (!dex_file_loader.Open(segment,
240 segment,
241 /* verify= */ true,
242 /* verify_checksum= */ true,
243 &error_msg,
244 &dex_files)) {
245 JVMTI_LOG(WARNING, env) << "Could not open " << segment << " for boot classpath extension: "
246 << error_msg;
247 return ERR(ILLEGAL_ARGUMENT);
248 }
249
250 art::ScopedObjectAccess soa(art::Thread::Current());
251 for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
252 current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), dex_file.release());
253 }
254
255 return ERR(NONE);
256 }
257
AddToDexClassLoaderInMemory(jvmtiEnv * jvmti_env,jobject classloader,const char * dex_bytes,jint dex_bytes_length)258 jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
259 jobject classloader,
260 const char* dex_bytes,
261 jint dex_bytes_length) {
262 if (jvmti_env == nullptr) {
263 return ERR(INVALID_ENVIRONMENT);
264 } else if (art::Thread::Current() == nullptr) {
265 return ERR(UNATTACHED_THREAD);
266 } else if (classloader == nullptr) {
267 return ERR(NULL_POINTER);
268 } else if (dex_bytes == nullptr) {
269 return ERR(NULL_POINTER);
270 } else if (dex_bytes_length <= 0) {
271 return ERR(ILLEGAL_ARGUMENT);
272 }
273
274 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
275
276 // TODO We really should try to support doing this during the ON_LOAD phase.
277 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
278 JVMTI_LOG(INFO, jvmti_env) << "Cannot add buffers to classpath during ON_LOAD phase to "
279 << "prevent file-descriptor leaking.";
280 return ERR(WRONG_PHASE);
281 }
282
283 // We have java APIs for adding files to the classpath, we might as well use them. It simplifies a
284 // lot of code as well.
285
286 // Create a memfd
287 art::File file(art::memfd_create_compat("JVMTI InMemory Added dex file", 0), /*check-usage*/true);
288 if (file.Fd() < 0) {
289 char* reason = strerror(errno);
290 JVMTI_LOG(ERROR, jvmti_env) << "Unable to create memfd due to " << reason;
291 if (file.FlushClose() < 0) {
292 PLOG(WARNING) << "Failed to close file!";
293 }
294 return ERR(INTERNAL);
295 }
296 // Fill it with the buffer.
297 if (!file.WriteFully(dex_bytes, dex_bytes_length) || file.Flush() != 0) {
298 JVMTI_LOG(ERROR, jvmti_env) << "Failed to write to memfd!";
299 if (file.FlushClose() < 0) {
300 PLOG(WARNING) << "Failed to close file!";
301 }
302 return ERR(INTERNAL);
303 }
304 // Get the filename in procfs.
305 std::ostringstream oss;
306 oss << "/proc/self/fd/" << file.Fd();
307 std::string seg(oss.str());
308 // Use common code.
309
310 jvmtiError result = AddToDexClassLoader(jvmti_env, classloader, seg.c_str());
311 // We have either loaded the dex file and have a new MemMap pointing to the same pages or loading
312 // has failed and the memory isn't needed anymore. Either way we can close the memfd we created
313 // and return.
314 if (file.Close() != 0) {
315 JVMTI_LOG(WARNING, jvmti_env) << "Failed to close memfd!";
316 }
317 return result;
318 }
319
AddToDexClassLoader(jvmtiEnv * jvmti_env,jobject classloader,const char * segment)320 jvmtiError SearchUtil::AddToDexClassLoader(jvmtiEnv* jvmti_env,
321 jobject classloader,
322 const char* segment) {
323 if (jvmti_env == nullptr) {
324 return ERR(INVALID_ENVIRONMENT);
325 } else if (art::Thread::Current() == nullptr) {
326 return ERR(UNATTACHED_THREAD);
327 } else if (classloader == nullptr) {
328 return ERR(NULL_POINTER);
329 } else if (segment == nullptr) {
330 return ERR(NULL_POINTER);
331 }
332
333 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
334
335 // TODO We really should try to support doing this during the ON_LOAD phase.
336 if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
337 JVMTI_LOG(INFO, jvmti_env) << "Cannot add to classpath of arbitrary classloaders during "
338 << "ON_LOAD phase.";
339 return ERR(WRONG_PHASE);
340 }
341
342 // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
343 // exceptions are swallowed.
344
345 art::Thread* self = art::Thread::Current();
346 JNIEnv* env = self->GetJniEnv();
347 if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
348 JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
349 return ERR(CLASS_LOADER_UNSUPPORTED);
350 }
351
352 jmethodID add_dex_path_id = env->GetMethodID(
353 art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
354 "addDexPath",
355 "(Ljava/lang/String;)V");
356 if (add_dex_path_id == nullptr) {
357 return ERR(INTERNAL);
358 }
359
360 ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
361 if (dex_path.get() == nullptr) {
362 return ERR(INTERNAL);
363 }
364 env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
365
366 if (env->ExceptionCheck()) {
367 {
368 art::ScopedObjectAccess soa(self);
369 JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
370 << self->GetException()->Dump();
371 }
372 env->ExceptionClear();
373 return ERR(ILLEGAL_ARGUMENT);
374 }
375 return OK;
376 }
377
AddToSystemClassLoaderSearch(jvmtiEnv * jvmti_env,const char * segment)378 jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env, const char* segment) {
379 if (segment == nullptr) {
380 return ERR(NULL_POINTER);
381 }
382
383 jvmtiPhase phase = PhaseUtil::GetPhaseUnchecked();
384
385 if (phase == jvmtiPhase::JVMTI_PHASE_ONLOAD) {
386 // We could try and see whether it is a valid path. We could also try to allocate Java
387 // objects to avoid later OOME.
388 gSystemOnloadSegments.push_back(segment);
389 return ERR(NONE);
390 } else if (phase != jvmtiPhase::JVMTI_PHASE_LIVE) {
391 return ERR(WRONG_PHASE);
392 }
393
394 jobject loader = art::Runtime::Current()->GetSystemClassLoader();
395 if (loader == nullptr) {
396 return ERR(INTERNAL);
397 }
398
399 art::Thread* self = art::Thread::Current();
400 JNIEnv* env = self->GetJniEnv();
401 if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
402 return ERR(INTERNAL);
403 }
404
405 return AddToDexClassLoader(jvmti_env, loader, segment);
406 }
407
408 } // namespace openjdkjvmti
409