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     .VMInit = CbVmInit,
291     .ClassFileLoadHook = CbClassFileLoadHook,
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