1 /*
2  * Copyright (C) 2017 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 <jni.h>
18 
19 #include <stack>
20 #include <string>
21 #include <unordered_map>
22 #include <vector>
23 
24 #include "android-base/logging.h"
25 #include "android-base/macros.h"
26 #include "jni_helper.h"
27 #include "jvmti_helper.h"
28 #include "jvmti.h"
29 #include "scoped_primitive_array.h"
30 #include "test_env.h"
31 
32 namespace art {
33 
Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jclass target,jbyteArray dex_bytes)34 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_redefineClass(
35     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jclass target, jbyteArray dex_bytes) {
36   jvmtiClassDefinition def;
37   def.klass = target;
38   def.class_byte_count = static_cast<jint>(env->GetArrayLength(dex_bytes));
39   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
40   jvmtiError res =jvmti_env->Allocate(def.class_byte_count,
41                                       const_cast<unsigned char**>(&def.class_bytes));
42   if (res != JVMTI_ERROR_NONE) {
43     return static_cast<jint>(res);
44   }
45   memcpy(const_cast<unsigned char*>(def.class_bytes), redef_bytes, def.class_byte_count);
46   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
47   // Do the redefinition.
48   res = jvmti_env->RedefineClasses(1, &def);
49   return static_cast<jint>(res);
50 }
51 
Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jclass target)52 extern "C" JNIEXPORT jint JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_retransformClass(
53     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jclass target) {
54   return jvmti_env->RetransformClasses(1, &target);
55 }
56 
57 class TransformationData {
58  public:
TransformationData()59   TransformationData() : redefinitions_(), should_pop_(false) {}
60 
SetPop(bool val)61   void SetPop(bool val) {
62     should_pop_ = val;
63   }
64 
ClearRedefinitions()65   void ClearRedefinitions() {
66     redefinitions_.clear();
67   }
68 
PushRedefinition(std::string name,std::vector<unsigned char> data)69   void PushRedefinition(std::string name, std::vector<unsigned char> data) {
70     if (redefinitions_.find(name) == redefinitions_.end()) {
71       std::stack<std::vector<unsigned char>> stack;
72       redefinitions_[name] = std::move(stack);
73     }
74     redefinitions_[name].push(std::move(data));
75   }
76 
RetrieveRedefinition(std::string name,std::vector<unsigned char> * data)77   bool RetrieveRedefinition(std::string name, /*out*/std::vector<unsigned char>* data) {
78     auto stack = redefinitions_.find(name);
79     if (stack == redefinitions_.end() || stack->second.empty()) {
80       return false;
81     } else {
82       *data = stack->second.top();
83       return true;
84     }
85   }
86 
PopRedefinition(std::string name)87   void PopRedefinition(std::string name) {
88     if (should_pop_) {
89       auto stack = redefinitions_.find(name);
90       if (stack == redefinitions_.end() || stack->second.empty()) {
91         return;
92       } else {
93         stack->second.pop();
94       }
95     }
96   }
97 
98  private:
99   std::unordered_map<std::string, std::stack<std::vector<unsigned char>>> redefinitions_;
100   bool should_pop_;
101 };
102 
103 static TransformationData data;
104 
105 // The hook we are using.
CommonClassFileLoadHookRetransformable(jvmtiEnv * local_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)106 void JNICALL CommonClassFileLoadHookRetransformable(jvmtiEnv* local_jvmti_env,
107                                                     JNIEnv* jni_env ATTRIBUTE_UNUSED,
108                                                     jclass class_being_redefined ATTRIBUTE_UNUSED,
109                                                     jobject loader ATTRIBUTE_UNUSED,
110                                                     const char* name,
111                                                     jobject protection_domain ATTRIBUTE_UNUSED,
112                                                     jint class_data_len ATTRIBUTE_UNUSED,
113                                                     const unsigned char* class_dat ATTRIBUTE_UNUSED,
114                                                     jint* new_class_data_len,
115                                                     unsigned char** new_class_data) {
116   std::string name_str(name);
117   std::vector<unsigned char> dex_data;
118   if (data.RetrieveRedefinition(name_str, &dex_data)) {
119     unsigned char* jvmti_dex_data;
120     if (JVMTI_ERROR_NONE != local_jvmti_env->Allocate(dex_data.size(), &jvmti_dex_data)) {
121       LOG(FATAL) << "Unable to allocate output buffer for " << name;
122       return;
123     }
124     memcpy(jvmti_dex_data, dex_data.data(), dex_data.size());
125     *new_class_data_len = dex_data.size();
126     *new_class_data = jvmti_dex_data;
127     data.PopRedefinition(name);
128   }
129 }
130 
131 extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jboolean enable)132 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setTransformationEvent(
133     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
134   jvmtiEventCallbacks cb;
135   memset(&cb, 0, sizeof(cb));
136   cb.ClassFileLoadHook = CommonClassFileLoadHookRetransformable;
137   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
138     return;
139   }
140   JvmtiErrorToException(env,
141                         jvmti_env,
142                         jvmti_env->SetEventNotificationMode(
143                             enable == JNI_TRUE ? JVMTI_ENABLE : JVMTI_DISABLE,
144                             JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
145                             nullptr));
146   return;
147 }
148 
149 extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED)150 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_clearTransformations(
151     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) {
152   data.ClearRedefinitions();
153 }
154 
155 extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(JNIEnv * env ATTRIBUTE_UNUSED,jclass klass ATTRIBUTE_UNUSED,jboolean enable)156 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_setPopTransformations(
157     JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
158   data.SetPop(enable == JNI_TRUE ? true : false);
159 }
160 
161 extern "C"
Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(JNIEnv * env,jclass klass ATTRIBUTE_UNUSED,jstring class_name,jbyteArray dex_bytes)162 JNIEXPORT void JNICALL Java_android_jvmti_cts_JvmtiRedefineClassesTest_pushTransformationResult(
163     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jstring class_name, jbyteArray dex_bytes) {
164   const char* name_chrs = env->GetStringUTFChars(class_name, nullptr);
165   std::string name_str(name_chrs);
166   env->ReleaseStringUTFChars(class_name, name_chrs);
167   std::vector<unsigned char> dex_data;
168   dex_data.resize(env->GetArrayLength(dex_bytes));
169   signed char* redef_bytes = env->GetByteArrayElements(dex_bytes, nullptr);
170   memcpy(dex_data.data(), redef_bytes, env->GetArrayLength(dex_bytes));
171   data.PushRedefinition(std::move(name_str), std::move(dex_data));
172   env->ReleaseByteArrayElements(dex_bytes, redef_bytes, 0);
173 }
174 
175 }  // namespace art
176 
177