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