1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15
16 #include "__mutex_base"
17 #include <cstddef>
18 #include <fcntl.h>
19 #include <fstream>
20 #include <memory>
21 #include <sstream>
22 #include <string>
23 #include <unistd.h>
24 #include <unordered_set>
25
26 #include <android-base/logging.h>
27 #include <android-base/macros.h>
28
29 #include <nativehelper/scoped_local_ref.h>
30
31 #include <jni.h>
32 #include <jvmti.h>
33
34 // Slicer's headers have code that triggers these warnings. b/65298177
35 #pragma clang diagnostic push
36 #pragma clang diagnostic ignored "-Wunused-parameter"
37 #pragma clang diagnostic ignored "-Wsign-compare"
38 #include <slicer/code_ir.h>
39 #include <slicer/dex_bytecode.h>
40 #include <slicer/dex_ir.h>
41 #include <slicer/dex_ir_builder.h>
42 #include <slicer/reader.h>
43 #include <slicer/writer.h>
44 #pragma clang diagnostic pop
45
46 namespace forceredefine {
47
48 namespace {
49
50 struct AgentInfo {
51 std::fstream stream;
52 std::unordered_set<std::string> classes;
53 std::mutex mutex;
54 };
55
56 // Converts a class name to a type descriptor
57 // (ex. "java.lang.String" to "Ljava/lang/String;")
classNameToDescriptor(const char * className)58 std::string classNameToDescriptor(const char* className) {
59 std::stringstream ss;
60 ss << "L";
61 for (auto p = className; *p != '\0'; ++p) {
62 ss << (*p == '.' ? '/' : *p);
63 }
64 ss << ";";
65 return ss.str();
66 }
67
68 // Converts a descriptor (Lthis/style/of/name;) to a jni-FindClass style Fully-qualified class name
69 // (this/style/of/name).
DescriptorToFQCN(const std::string & descriptor)70 std::string DescriptorToFQCN(const std::string& descriptor) {
71 return descriptor.substr(1, descriptor.size() - 2);
72 }
73
GetAgentInfo(jvmtiEnv * jvmti)74 static AgentInfo* GetAgentInfo(jvmtiEnv* jvmti) {
75 AgentInfo* ai = nullptr;
76 CHECK_EQ(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&ai)), JVMTI_ERROR_NONE);
77 CHECK(ai != nullptr);
78 return ai;
79 }
80
81 class JvmtiAllocator : public dex::Writer::Allocator {
82 public:
JvmtiAllocator(jvmtiEnv * jvmti)83 explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {}
Allocate(size_t size)84 void* Allocate(size_t size) override {
85 unsigned char* res = nullptr;
86 jvmti_->Allocate(size, &res);
87 return res;
88 }
Free(void * ptr)89 void Free(void* ptr) override {
90 jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
91 }
92
93 private:
94 jvmtiEnv* jvmti_;
95 };
96
Transform(std::shared_ptr<ir::DexFile> ir)97 static void Transform(std::shared_ptr<ir::DexFile> ir) {
98 std::unique_ptr<ir::Builder> builder;
99 for (auto& method : ir->encoded_methods) {
100 // Do not look into abstract/bridge/native/synthetic methods.
101 if ((method->access_flags &
102 (dex::kAccAbstract | dex::kAccBridge | dex::kAccNative | dex::kAccSynthetic)) != 0) {
103 continue;
104 }
105
106 struct AddNopVisitor : public lir::Visitor {
107 explicit AddNopVisitor(lir::CodeIr* cir) : cir_(cir) {}
108
109 bool Visit(lir::Bytecode* bc) override {
110 if (seen_first_inst) {
111 return false;
112 }
113 seen_first_inst = true;
114 auto new_inst = cir_->Alloc<lir::Bytecode>();
115 new_inst->opcode = dex::OP_NOP;
116 cir_->instructions.InsertBefore(bc, new_inst);
117 return true;
118 }
119
120 lir::CodeIr* cir_;
121 bool seen_first_inst = false;
122 };
123
124 lir::CodeIr c(method.get(), ir);
125 AddNopVisitor visitor(&c);
126 for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) {
127 lir::Instruction* fi = *it;
128 if (fi->Accept(&visitor)) {
129 break;
130 }
131 }
132 c.Assemble();
133 }
134 }
135
CbClassFileLoadHook(jvmtiEnv * jvmti,JNIEnv * env ATTRIBUTE_UNUSED,jclass classBeingRedefined ATTRIBUTE_UNUSED,jobject loader ATTRIBUTE_UNUSED,const char * name,jobject protectionDomain ATTRIBUTE_UNUSED,jint classDataLen,const unsigned char * classData,jint * newClassDataLen,unsigned char ** newClassData)136 static void CbClassFileLoadHook(jvmtiEnv* jvmti,
137 JNIEnv* env ATTRIBUTE_UNUSED,
138 jclass classBeingRedefined ATTRIBUTE_UNUSED,
139 jobject loader ATTRIBUTE_UNUSED,
140 const char* name,
141 jobject protectionDomain ATTRIBUTE_UNUSED,
142 jint classDataLen,
143 const unsigned char* classData,
144 jint* newClassDataLen,
145 unsigned char** newClassData) {
146 std::string desc(classNameToDescriptor(name));
147 std::string fqcn(DescriptorToFQCN(desc));
148 AgentInfo* ai = GetAgentInfo(jvmti);
149 {
150 std::lock_guard<std::mutex> mu(ai->mutex);
151 if (ai->classes.find(fqcn) == ai->classes.end()) {
152 return;
153 }
154 }
155 LOG(INFO) << "Got CFLH for " << name << " on env " << static_cast<void*>(jvmti);
156 JvmtiAllocator allocator(jvmti);
157 dex::Reader reader(classData, classDataLen);
158 dex::u4 index = reader.FindClassIndex(desc.c_str());
159 reader.CreateClassIr(index);
160 std::shared_ptr<ir::DexFile> ir(reader.GetIr());
161 Transform(ir);
162 dex::Writer writer(ir);
163 size_t new_size;
164 *newClassData = writer.CreateImage(&allocator, &new_size);
165 *newClassDataLen = new_size;
166 }
167
FindClass(jvmtiEnv * jvmti,JNIEnv * env,const std::string & name)168 static jclass FindClass(jvmtiEnv* jvmti, JNIEnv* env, const std::string& name) {
169 jclass res = env->FindClass(name.c_str());
170 if (res != nullptr) {
171 return res;
172 }
173 ScopedLocalRef<jthrowable> exc(env, env->ExceptionOccurred());
174 env->ExceptionClear();
175 // Try to find it in other classloaders.
176 env->PushLocalFrame(1 << 18);
177 do {
178 jint cnt;
179 jclass* klasses;
180 if (jvmti->GetLoadedClasses(&cnt, &klasses) != JVMTI_ERROR_NONE) {
181 LOG(ERROR) << "Unable to get loaded classes!";
182 break;
183 }
184 for (jint i = 0; i < cnt; i++) {
185 char* sig;
186 if (jvmti->GetClassSignature(klasses[i], &sig, nullptr) != JVMTI_ERROR_NONE) {
187 continue;
188 }
189 if (sig[0] == 'L' && DescriptorToFQCN(sig) == name) {
190 res = klasses[i];
191 break;
192 }
193 }
194 jvmti->Deallocate(reinterpret_cast<unsigned char*>(klasses));
195 } while (false);
196 res = reinterpret_cast<jclass>(env->PopLocalFrame(res));
197 if (res == nullptr && exc.get() != nullptr) {
198 env->Throw(exc.get());
199 }
200 return res;
201 }
202
RedefineClass(jvmtiEnv * jvmti,JNIEnv * env,const std::string & klass_name)203 static void RedefineClass(jvmtiEnv* jvmti, JNIEnv* env, const std::string& klass_name) {
204 jclass klass = nullptr;
205 if ((klass = FindClass(jvmti, env, klass_name)) == nullptr) {
206 LOG(WARNING) << "Failed to find class for " << klass_name;
207 env->ExceptionDescribe();
208 env->ExceptionClear();
209 return;
210 }
211 jvmti->RetransformClasses(1, &klass);
212 env->DeleteLocalRef(klass);
213 }
214
AgentMain(jvmtiEnv * jvmti,JNIEnv * jni,void * arg ATTRIBUTE_UNUSED)215 static void AgentMain(jvmtiEnv* jvmti, JNIEnv* jni, void* arg ATTRIBUTE_UNUSED) {
216 AgentInfo* ai = GetAgentInfo(jvmti);
217 std::string klass_name;
218 jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr);
219 // TODO Replace this with something that can read from a fifo and ignore the 'EOF's.
220 while (std::getline(ai->stream, klass_name, '\n')) {
221 LOG(INFO) << "Redefining class " << klass_name << " with " << static_cast<void*>(jvmti);
222 {
223 std::lock_guard<std::mutex> mu(ai->mutex);
224 ai->classes.insert(klass_name);
225 }
226 RedefineClass(jvmti, jni, klass_name);
227 }
228 }
229
CbVmInit(jvmtiEnv * jvmti,JNIEnv * env,jthread thr ATTRIBUTE_UNUSED)230 static void CbVmInit(jvmtiEnv* jvmti, JNIEnv* env, jthread thr ATTRIBUTE_UNUSED) {
231 // Create a Thread object.
232 ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread"));
233 if (thread_name.get() == nullptr) {
234 env->ExceptionDescribe();
235 env->ExceptionClear();
236 return;
237 }
238 ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
239 if (thread_klass.get() == nullptr) {
240 env->ExceptionDescribe();
241 env->ExceptionClear();
242 return;
243 }
244 ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
245 if (thread.get() == nullptr) {
246 env->ExceptionDescribe();
247 env->ExceptionClear();
248 return;
249 }
250
251 env->CallNonvirtualVoidMethod(
252 thread.get(),
253 thread_klass.get(),
254 env->GetMethodID(thread_klass.get(), "<init>", "(Ljava/lang/String;)V"),
255 thread_name.get());
256 env->CallVoidMethod(thread.get(), env->GetMethodID(thread_klass.get(), "setPriority", "(I)V"), 1);
257 env->CallVoidMethod(
258 thread.get(), env->GetMethodID(thread_klass.get(), "setDaemon", "(Z)V"), JNI_TRUE);
259
260 jvmti->RunAgentThread(thread.get(), AgentMain, nullptr, JVMTI_THREAD_MIN_PRIORITY);
261 }
262
263 } // namespace
264
265 template <bool kIsOnLoad>
AgentStart(JavaVM * vm,char * options,void * reserved ATTRIBUTE_UNUSED)266 static jint AgentStart(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
267 jvmtiEnv* jvmti = nullptr;
268
269 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1) != JNI_OK ||
270 jvmti == nullptr) {
271 LOG(ERROR) << "unable to obtain JVMTI env.";
272 return JNI_ERR;
273 }
274 std::string sopts(options);
275 AgentInfo* ai = new AgentInfo;
276 ai->stream.open(options, std::ios_base::in);
277 if (!ai->stream.is_open()) {
278 PLOG(ERROR) << "Could not open file " << options << " for triggering class-reload";
279 return JNI_ERR;
280 }
281
282 jvmtiCapabilities caps{
283 .can_retransform_classes = 1,
284 };
285 if (jvmti->AddCapabilities(&caps) != JVMTI_ERROR_NONE) {
286 LOG(ERROR) << "Unable to get retransform_classes capability!";
287 return JNI_ERR;
288 }
289 jvmtiEventCallbacks cb{
290 .ClassFileLoadHook = CbClassFileLoadHook,
291 .VMInit = CbVmInit,
292 };
293 jvmti->SetEventCallbacks(&cb, sizeof(cb));
294 jvmti->SetEnvironmentLocalStorage(reinterpret_cast<void*>(ai));
295 if (kIsOnLoad) {
296 jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr);
297 } else {
298 JNIEnv* jni = nullptr;
299 vm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_2);
300 jthread thr;
301 jvmti->GetCurrentThread(&thr);
302 CbVmInit(jvmti, jni, thr);
303 }
304 return JNI_OK;
305 }
306
307 // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)308 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
309 return AgentStart<false>(vm, options, reserved);
310 }
311
312 // Early attachment
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)313 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
314 return AgentStart<true>(jvm, options, reserved);
315 }
316
317 } // namespace forceredefine
318