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