1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "common_helper.h"
18
19 #include <dlfcn.h>
20 #include <map>
21 #include <stdio.h>
22 #include <sstream>
23 #include <deque>
24 #include <vector>
25
26 #include "android-base/stringprintf.h"
27 #include "jni.h"
28 #include "jvmti.h"
29
30 #include "jni_binder.h"
31 #include "jvmti_helper.h"
32 #include "scoped_local_ref.h"
33 #include "test_env.h"
34
35 namespace art {
36
37 static void SetupCommonRetransform();
38 static void SetupCommonRedefine();
39 static void SetupCommonTransform();
40
41 template <bool is_redefine>
throwCommonRedefinitionError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * target,jvmtiError res)42 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
43 JNIEnv* env,
44 jint num_targets,
45 jclass* target,
46 jvmtiError res) {
47 std::stringstream err;
48 char* error = nullptr;
49 jvmti->GetErrorName(res, &error);
50 err << "Failed to " << (is_redefine ? "redefine" : "retransform") << " class";
51 if (num_targets > 1) {
52 err << "es";
53 }
54 err << " <";
55 for (jint i = 0; i < num_targets; i++) {
56 char* signature = nullptr;
57 char* generic = nullptr;
58 jvmti->GetClassSignature(target[i], &signature, &generic);
59 if (i != 0) {
60 err << ", ";
61 }
62 err << signature;
63 jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
64 jvmti->Deallocate(reinterpret_cast<unsigned char*>(generic));
65 }
66 err << "> due to " << error;
67 std::string message = err.str();
68 jvmti->Deallocate(reinterpret_cast<unsigned char*>(error));
69 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
70 }
71
72 namespace common_redefine {
73
throwRedefinitionError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * target,jvmtiError res)74 static void throwRedefinitionError(jvmtiEnv* jvmti,
75 JNIEnv* env,
76 jint num_targets,
77 jclass* target,
78 jvmtiError res) {
79 return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
80 }
81
DoMultiClassRedefine(jvmtiEnv * jvmti_env,JNIEnv * env,jint num_redefines,jclass * targets,jbyteArray * class_file_bytes,jbyteArray * dex_file_bytes)82 static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
83 JNIEnv* env,
84 jint num_redefines,
85 jclass* targets,
86 jbyteArray* class_file_bytes,
87 jbyteArray* dex_file_bytes) {
88 std::vector<jvmtiClassDefinition> defs;
89 for (jint i = 0; i < num_redefines; i++) {
90 jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
91 jint len = static_cast<jint>(env->GetArrayLength(desired_array));
92 const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
93 env->GetByteArrayElements(desired_array, nullptr));
94 defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
95 }
96 jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
97 if (res != JVMTI_ERROR_NONE) {
98 throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
99 }
100 }
101
DoClassRedefine(jvmtiEnv * jvmti_env,JNIEnv * env,jclass target,jbyteArray class_file_bytes,jbyteArray dex_file_bytes)102 static void DoClassRedefine(jvmtiEnv* jvmti_env,
103 JNIEnv* env,
104 jclass target,
105 jbyteArray class_file_bytes,
106 jbyteArray dex_file_bytes) {
107 return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
108 }
109
110 // Magic JNI export that classes can use for redefining classes.
111 // To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
Java_art_Redefinition_doCommonClassRedefinition(JNIEnv * env,jclass,jclass target,jbyteArray class_file_bytes,jbyteArray dex_file_bytes)112 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
113 JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
114 DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
115 }
116
117 // Magic JNI export that classes can use for redefining classes.
118 // To use classes should declare this as a native function with signature
119 // ([Ljava/lang/Class;[[B[[B)V
Java_art_Redefinition_doCommonMultiClassRedefinition(JNIEnv * env,jclass,jobjectArray targets,jobjectArray class_file_bytes,jobjectArray dex_file_bytes)120 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefinition(
121 JNIEnv* env,
122 jclass,
123 jobjectArray targets,
124 jobjectArray class_file_bytes,
125 jobjectArray dex_file_bytes) {
126 std::vector<jclass> classes;
127 std::vector<jbyteArray> class_files;
128 std::vector<jbyteArray> dex_files;
129 jint len = env->GetArrayLength(targets);
130 if (len != env->GetArrayLength(class_file_bytes) || len != env->GetArrayLength(dex_file_bytes)) {
131 env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
132 "the three array arguments passed to this function have different lengths!");
133 return;
134 }
135 for (jint i = 0; i < len; i++) {
136 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
137 dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
138 class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
139 }
140 return DoMultiClassRedefine(jvmti_env,
141 env,
142 len,
143 classes.data(),
144 class_files.data(),
145 dex_files.data());
146 }
147
148 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options ATTRIBUTE_UNUSED,void * reserved ATTRIBUTE_UNUSED)149 jint OnLoad(JavaVM* vm,
150 char* options ATTRIBUTE_UNUSED,
151 void* reserved ATTRIBUTE_UNUSED) {
152 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
153 printf("Unable to get jvmti env!\n");
154 return 1;
155 }
156 SetupCommonRedefine();
157 return 0;
158 }
159
160 } // namespace common_redefine
161
162 namespace common_retransform {
163
164 struct CommonTransformationResult {
165 std::vector<unsigned char> class_bytes;
166 std::vector<unsigned char> dex_bytes;
167
CommonTransformationResultart::common_retransform::CommonTransformationResult168 CommonTransformationResult(size_t class_size, size_t dex_size)
169 : class_bytes(class_size), dex_bytes(dex_size) {}
170
171 CommonTransformationResult() = default;
172 CommonTransformationResult(CommonTransformationResult&&) = default;
173 CommonTransformationResult(CommonTransformationResult&) = default;
174 };
175
176 // Map from class name to transformation result.
177 std::map<std::string, std::deque<CommonTransformationResult>> gTransformations;
178 bool gPopTransformations = true;
179
Java_art_Redefinition_addCommonTransformationResult(JNIEnv * env,jclass,jstring class_name,jbyteArray class_array,jbyteArray dex_array)180 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_addCommonTransformationResult(
181 JNIEnv* env, jclass, jstring class_name, jbyteArray class_array, jbyteArray dex_array) {
182 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
183 std::string name_str(name_chrs);
184 env->ReleaseStringUTFChars(class_name, name_chrs);
185 CommonTransformationResult trans(env->GetArrayLength(class_array),
186 env->GetArrayLength(dex_array));
187 if (env->ExceptionOccurred()) {
188 return;
189 }
190 env->GetByteArrayRegion(class_array,
191 0,
192 env->GetArrayLength(class_array),
193 reinterpret_cast<jbyte*>(trans.class_bytes.data()));
194 if (env->ExceptionOccurred()) {
195 return;
196 }
197 env->GetByteArrayRegion(dex_array,
198 0,
199 env->GetArrayLength(dex_array),
200 reinterpret_cast<jbyte*>(trans.dex_bytes.data()));
201 if (env->ExceptionOccurred()) {
202 return;
203 }
204 if (gTransformations.find(name_str) == gTransformations.end()) {
205 std::deque<CommonTransformationResult> list;
206 gTransformations[name_str] = std::move(list);
207 }
208 gTransformations[name_str].push_back(std::move(trans));
209 }
210
211 // The hook we are using.
CommonClassFileLoadHookRetransformable(jvmtiEnv * jvmti_env,JNIEnv * jni_env ATTRIBUTE_UNUSED,jclass class_being_redefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protection_domain ATTRIBUTE_UNUSED,jint class_data_len ATTRIBUTE_UNUSED,const unsigned char * class_dat ATTRIBUTE_UNUSED,jint * new_class_data_len,unsigned char ** new_class_data)212 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* jvmti_env,
213 JNIEnv* jni_env ATTRIBUTE_UNUSED,
214 jclass class_being_redefined ATTRIBUTE_UNUSED,
215 jobject loader ATTRIBUTE_UNUSED,
216 const char* name,
217 jobject protection_domain ATTRIBUTE_UNUSED,
218 jint class_data_len ATTRIBUTE_UNUSED,
219 const unsigned char* class_dat ATTRIBUTE_UNUSED,
220 jint* new_class_data_len,
221 unsigned char** new_class_data) {
222 std::string name_str(name);
223 if (gTransformations.find(name_str) != gTransformations.end() &&
224 gTransformations[name_str].size() > 0) {
225 CommonTransformationResult& res = gTransformations[name_str][0];
226 const std::vector<unsigned char>& desired_array = IsJVM() ? res.class_bytes : res.dex_bytes;
227 unsigned char* new_data;
228 CHECK_EQ(JVMTI_ERROR_NONE, jvmti_env->Allocate(desired_array.size(), &new_data));
229 memcpy(new_data, desired_array.data(), desired_array.size());
230 *new_class_data = new_data;
231 *new_class_data_len = desired_array.size();
232 if (gPopTransformations) {
233 gTransformations[name_str].pop_front();
234 }
235 }
236 }
237
Java_art_Redefinition_setPopRetransformations(JNIEnv *,jclass,jboolean enable)238 extern "C" JNIEXPORT void Java_art_Redefinition_setPopRetransformations(JNIEnv*,
239 jclass,
240 jboolean enable) {
241 gPopTransformations = enable;
242 }
243
Java_art_Redefinition_popTransformationFor(JNIEnv * env,jclass,jstring class_name)244 extern "C" JNIEXPORT void Java_art_Redefinition_popTransformationFor(JNIEnv* env,
245 jclass,
246 jstring class_name) {
247 const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
248 std::string name_str(name_chrs);
249 env->ReleaseStringUTFChars(class_name, name_chrs);
250 if (gTransformations.find(name_str) != gTransformations.end() &&
251 gTransformations[name_str].size() > 0) {
252 gTransformations[name_str].pop_front();
253 } else {
254 std::stringstream err;
255 err << "No transformations found for class " << name_str;
256 std::string message = err.str();
257 env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
258 }
259 }
260
Java_art_Redefinition_enableCommonRetransformation(JNIEnv * env,jclass,jboolean enable)261 extern "C" JNIEXPORT void Java_art_Redefinition_enableCommonRetransformation(JNIEnv* env,
262 jclass,
263 jboolean enable) {
264 jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE,
265 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
266 nullptr);
267 if (res != JVMTI_ERROR_NONE) {
268 JvmtiErrorToException(env, jvmti_env, res);
269 }
270 }
271
throwRetransformationError(jvmtiEnv * jvmti,JNIEnv * env,jint num_targets,jclass * targets,jvmtiError res)272 static void throwRetransformationError(jvmtiEnv* jvmti,
273 JNIEnv* env,
274 jint num_targets,
275 jclass* targets,
276 jvmtiError res) {
277 return throwCommonRedefinitionError<false>(jvmti, env, num_targets, targets, res);
278 }
279
DoClassRetransformation(jvmtiEnv * jvmti_env,JNIEnv * env,jobjectArray targets)280 static void DoClassRetransformation(jvmtiEnv* jvmti_env, JNIEnv* env, jobjectArray targets) {
281 std::vector<jclass> classes;
282 jint len = env->GetArrayLength(targets);
283 for (jint i = 0; i < len; i++) {
284 classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
285 }
286 jvmtiError res = jvmti_env->RetransformClasses(len, classes.data());
287 if (res != JVMTI_ERROR_NONE) {
288 throwRetransformationError(jvmti_env, env, len, classes.data(), res);
289 }
290 }
291
Java_art_Redefinition_doCommonClassRetransformation(JNIEnv * env,jclass,jobjectArray targets)292 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRetransformation(
293 JNIEnv* env, jclass, jobjectArray targets) {
294 jvmtiCapabilities caps;
295 jvmtiError caps_err = jvmti_env->GetCapabilities(&caps);
296 if (caps_err != JVMTI_ERROR_NONE) {
297 env->ThrowNew(env->FindClass("java/lang/Exception"),
298 "Unable to get current jvmtiEnv capabilities");
299 return;
300 }
301
302 // Allocate a new environment if we don't have the can_retransform_classes capability needed to
303 // call the RetransformClasses function.
304 jvmtiEnv* real_env = nullptr;
305 if (caps.can_retransform_classes != 1) {
306 JavaVM* vm = nullptr;
307 if (env->GetJavaVM(&vm) != 0 ||
308 vm->GetEnv(reinterpret_cast<void**>(&real_env), JVMTI_VERSION_1_0) != 0) {
309 env->ThrowNew(env->FindClass("java/lang/Exception"),
310 "Unable to create temporary jvmtiEnv for RetransformClasses call.");
311 return;
312 }
313 SetAllCapabilities(real_env);
314 } else {
315 real_env = jvmti_env;
316 }
317 DoClassRetransformation(real_env, env, targets);
318 if (caps.can_retransform_classes != 1) {
319 real_env->DisposeEnvironment();
320 }
321 }
322
323 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options ATTRIBUTE_UNUSED,void * reserved ATTRIBUTE_UNUSED)324 jint OnLoad(JavaVM* vm,
325 char* options ATTRIBUTE_UNUSED,
326 void* reserved ATTRIBUTE_UNUSED) {
327 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
328 printf("Unable to get jvmti env!\n");
329 return 1;
330 }
331 SetupCommonRetransform();
332 return 0;
333 }
334
335 } // namespace common_retransform
336
337 namespace common_transform {
338
339 // Get all capabilities except those related to retransformation.
OnLoad(JavaVM * vm,char * options ATTRIBUTE_UNUSED,void * reserved ATTRIBUTE_UNUSED)340 jint OnLoad(JavaVM* vm,
341 char* options ATTRIBUTE_UNUSED,
342 void* reserved ATTRIBUTE_UNUSED) {
343 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
344 printf("Unable to get jvmti env!\n");
345 return 1;
346 }
347 SetupCommonTransform();
348 return 0;
349 }
350
351 } // namespace common_transform
352
353 #define CONFIGURATION_COMMON_REDEFINE 0
354 #define CONFIGURATION_COMMON_RETRANSFORM 1
355 #define CONFIGURATION_COMMON_TRANSFORM 2
356
SetupCommonRedefine()357 static void SetupCommonRedefine() {
358 jvmtiCapabilities caps;
359 jvmti_env->GetPotentialCapabilities(&caps);
360 caps.can_retransform_classes = 0;
361 caps.can_retransform_any_class = 0;
362 jvmti_env->AddCapabilities(&caps);
363 }
364
SetupCommonRetransform()365 static void SetupCommonRetransform() {
366 SetAllCapabilities(jvmti_env);
367 jvmtiEventCallbacks cb;
368 memset(&cb, 0, sizeof(cb));
369 cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
370 jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
371 CHECK_EQ(res, JVMTI_ERROR_NONE);
372 common_retransform::gTransformations.clear();
373 }
374
SetupCommonTransform()375 static void SetupCommonTransform() {
376 // Don't set the retransform caps
377 jvmtiCapabilities caps;
378 jvmti_env->GetPotentialCapabilities(&caps);
379 caps.can_retransform_classes = 0;
380 caps.can_retransform_any_class = 0;
381 jvmti_env->AddCapabilities(&caps);
382
383 // Use the same callback as the retransform test.
384 jvmtiEventCallbacks cb;
385 memset(&cb, 0, sizeof(cb));
386 cb.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
387 jvmtiError res = jvmti_env->SetEventCallbacks(&cb, sizeof(cb));
388 CHECK_EQ(res, JVMTI_ERROR_NONE);
389 common_retransform::gTransformations.clear();
390 }
391
Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv *,jclass,jint type)392 extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
393 jclass,
394 jint type) {
395 switch (type) {
396 case CONFIGURATION_COMMON_REDEFINE: {
397 SetupCommonRedefine();
398 return;
399 }
400 case CONFIGURATION_COMMON_RETRANSFORM: {
401 SetupCommonRetransform();
402 return;
403 }
404 case CONFIGURATION_COMMON_TRANSFORM: {
405 SetupCommonTransform();
406 return;
407 }
408 default: {
409 LOG(FATAL) << "Unknown test configuration: " << type;
410 }
411 }
412 }
413 } // namespace art
414