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