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