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(¤t_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(¤t_callbacks, sizeof(current_callbacks));
400 CHECK_EQ(res, JVMTI_ERROR_NONE);
401 common_retransform::gTransformations.clear();
402 }
403
404 } // namespace art
405