• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 <cstring>
18 #include <iostream>
19 #include <memory>
20 #include <sstream>
21 
22 #include <unistd.h>
23 
24 #include <jni.h>
25 
26 #include <jvmti.h>
27 
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/macros.h>
31 #include <android-base/strings.h>
32 #include <android-base/unique_fd.h>
33 
34 #include <fcntl.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 
38 #include <nativehelper/scoped_utf_chars.h>
39 
40 // We need dladdr.
41 #if !defined(__APPLE__) && !defined(_WIN32)
42 #ifndef _GNU_SOURCE
43 #define _GNU_SOURCE
44 #define DEFINED_GNU_SOURCE
45 #endif
46 #include <dlfcn.h>
47 #ifdef DEFINED_GNU_SOURCE
48 #undef _GNU_SOURCE
49 #undef DEFINED_GNU_SOURCE
50 #endif
51 #endif
52 
53 // Slicer's headers have code that triggers these warnings. b/65298177
54 #pragma clang diagnostic push
55 #pragma clang diagnostic ignored "-Wunused-parameter"
56 #pragma clang diagnostic ignored "-Wsign-compare"
57 
58 #include <slicer/dex_ir.h>
59 #include <slicer/code_ir.h>
60 #include <slicer/dex_bytecode.h>
61 #include <slicer/dex_ir_builder.h>
62 #include <slicer/writer.h>
63 #include <slicer/reader.h>
64 
65 #pragma clang diagnostic pop
66 
67 namespace {
68 
69 JavaVM* gJavaVM = nullptr;
70 bool gForkCrash = false;
71 bool gJavaCrash = false;
72 
73 // Converts a class name to a type descriptor
74 // (ex. "java.lang.String" to "Ljava/lang/String;")
classNameToDescriptor(const char * className)75 std::string classNameToDescriptor(const char* className) {
76     std::stringstream ss;
77     ss << "L";
78     for (auto p = className; *p != '\0'; ++p) {
79         ss << (*p == '.' ? '/' : *p);
80     }
81     ss << ";";
82     return ss.str();
83 }
84 
85 using namespace dex;
86 using namespace lir;
87 
88 class Transformer {
89 public:
Transformer(std::shared_ptr<ir::DexFile> dexIr)90     explicit Transformer(std::shared_ptr<ir::DexFile> dexIr) : dexIr_(dexIr) {}
91 
transform()92     bool transform() {
93         bool classModified = false;
94 
95         std::unique_ptr<ir::Builder> builder;
96 
97         for (auto& method : dexIr_->encoded_methods) {
98             // Do not look into abstract/bridge/native/synthetic methods.
99             if ((method->access_flags & (kAccAbstract | kAccBridge | kAccNative | kAccSynthetic))
100                     != 0) {
101                 continue;
102             }
103 
104             struct HookVisitor: public Visitor {
105                 HookVisitor(Transformer* transformer, CodeIr* c_ir)
106                         : transformer(transformer), cIr(c_ir) {
107                 }
108 
109                 bool Visit(Bytecode* bytecode) override {
110                     if (bytecode->opcode == OP_MONITOR_ENTER) {
111                         insertHook(bytecode, true,
112                                 reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
113                         return true;
114                     }
115                     if (bytecode->opcode == OP_MONITOR_EXIT) {
116                         insertHook(bytecode, false,
117                                 reinterpret_cast<VReg*>(bytecode->operands[0])->reg);
118                         return true;
119                     }
120                     return false;
121                 }
122 
123                 void insertHook(lir::Instruction* before, bool pre, u4 reg) {
124                     transformer->preparePrePost();
125                     transformer->addCall(cIr, before, OP_INVOKE_STATIC_RANGE,
126                             transformer->hookType_, pre ? "preLock" : "postLock",
127                             transformer->voidType_, transformer->objectType_, reg);
128                     myModified = true;
129                 }
130 
131                 Transformer* transformer;
132                 CodeIr* cIr;
133                 bool myModified = false;
134             };
135 
136             CodeIr c(method.get(), dexIr_);
137             bool methodModified = false;
138 
139             HookVisitor visitor(this, &c);
140             for (auto it = c.instructions.begin(); it != c.instructions.end(); ++it) {
141                 lir::Instruction* fi = *it;
142                 fi->Accept(&visitor);
143             }
144             methodModified |= visitor.myModified;
145 
146             if (methodModified) {
147                 classModified = true;
148                 c.Assemble();
149             }
150         }
151 
152         return classModified;
153     }
154 
155 private:
preparePrePost()156     void preparePrePost() {
157         // Insert "void LockHook.(pre|post)(Object o)."
158 
159         prepareBuilder();
160 
161         if (voidType_ == nullptr) {
162             voidType_ = builder_->GetType("V");
163         }
164         if (hookType_ == nullptr) {
165             hookType_ = builder_->GetType("Lcom/android/lock_checker/LockHook;");
166         }
167         if (objectType_ == nullptr) {
168             objectType_ = builder_->GetType("Ljava/lang/Object;");
169         }
170     }
171 
prepareBuilder()172     void prepareBuilder() {
173         if (builder_ == nullptr) {
174             builder_ = std::unique_ptr<ir::Builder>(new ir::Builder(dexIr_));
175         }
176     }
177 
addInst(CodeIr * cIr,lir::Instruction * instructionAfter,Opcode opcode,const std::list<Operand * > & operands)178     static void addInst(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode,
179             const std::list<Operand*>& operands) {
180         auto instruction = cIr->Alloc<Bytecode>();
181 
182         instruction->opcode = opcode;
183 
184         for (auto it = operands.begin(); it != operands.end(); it++) {
185             instruction->operands.push_back(*it);
186         }
187 
188         cIr->instructions.InsertBefore(instructionAfter, instruction);
189     }
190 
addCall(CodeIr * cIr,lir::Instruction * instructionAfter,Opcode opcode,ir::Type * type,const char * methodName,ir::Type * returnType,const std::vector<ir::Type * > & types,const std::list<int> & regs)191     void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
192             const char* methodName, ir::Type* returnType,
193             const std::vector<ir::Type*>& types, const std::list<int>& regs) {
194         auto proto = builder_->GetProto(returnType, builder_->GetTypeList(types));
195         auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
196 
197         VRegList* paramRegs = cIr->Alloc<VRegList>();
198         for (auto it = regs.begin(); it != regs.end(); it++) {
199             paramRegs->registers.push_back(*it);
200         }
201 
202         addInst(cIr, instructionAfter, opcode,
203                 { paramRegs, cIr->Alloc<Method>(method, method->orig_index) });
204     }
205 
addCall(CodeIr * cIr,lir::Instruction * instructionAfter,Opcode opcode,ir::Type * type,const char * methodName,ir::Type * returnType,ir::Type * paramType,u4 paramVReg)206     void addCall(CodeIr* cIr, lir::Instruction* instructionAfter, Opcode opcode, ir::Type* type,
207             const char* methodName, ir::Type* returnType, ir::Type* paramType,
208             u4 paramVReg) {
209         auto proto = builder_->GetProto(returnType, builder_->GetTypeList( { paramType }));
210         auto method = builder_->GetMethodDecl(builder_->GetAsciiString(methodName), proto, type);
211 
212         VRegRange* args = cIr->Alloc<VRegRange>(paramVReg, 1);
213 
214         addInst(cIr, instructionAfter, opcode,
215                 { args, cIr->Alloc<Method>(method, method->orig_index) });
216     }
217 
218     std::shared_ptr<ir::DexFile> dexIr_;
219     std::unique_ptr<ir::Builder> builder_;
220 
221     ir::Type* voidType_ = nullptr;
222     ir::Type* hookType_ = nullptr;
223     ir::Type* objectType_ = nullptr;
224 };
225 
maybeTransform(const char * name,size_t classDataLen,const unsigned char * classData,dex::Writer::Allocator * allocator)226 std::pair<dex::u1*, size_t> maybeTransform(const char* name, size_t classDataLen,
227         const unsigned char* classData, dex::Writer::Allocator* allocator) {
228     // Isolate byte code of class class. This is needed as Android usually gives us more
229     // than the class we need.
230     dex::Reader reader(classData, classDataLen);
231 
232     dex::u4 index = reader.FindClassIndex(classNameToDescriptor(name).c_str());
233     CHECK_NE(index, kNoIndex);
234     reader.CreateClassIr(index);
235     std::shared_ptr<ir::DexFile> ir = reader.GetIr();
236 
237     {
238         Transformer transformer(ir);
239         if (!transformer.transform()) {
240             return std::make_pair(nullptr, 0);
241         }
242     }
243 
244     size_t new_size;
245     dex::Writer writer(ir);
246     dex::u1* newClassData = writer.CreateImage(allocator, &new_size);
247     return std::make_pair(newClassData, new_size);
248 }
249 
transformHook(jvmtiEnv * jvmtiEnv,JNIEnv * env ATTRIBUTE_UNUSED,jclass classBeingRedefined ATTRIBUTE_UNUSED,jobject loader,const char * name,jobject protectionDomain ATTRIBUTE_UNUSED,jint classDataLen,const unsigned char * classData,jint * newClassDataLen,unsigned char ** newClassData)250 void transformHook(jvmtiEnv* jvmtiEnv, JNIEnv* env ATTRIBUTE_UNUSED,
251         jclass classBeingRedefined ATTRIBUTE_UNUSED, jobject loader, const char* name,
252         jobject protectionDomain ATTRIBUTE_UNUSED, jint classDataLen,
253         const unsigned char* classData, jint* newClassDataLen, unsigned char** newClassData) {
254     // Even reading the classData array is expensive as the data is only generated when the
255     // memory is touched. Hence call JvmtiAgent#shouldTransform to check if we need to transform
256     // the class.
257 
258     // Skip bootclasspath classes. TODO: Make this configurable.
259     if (loader == nullptr) {
260         return;
261     }
262 
263     // Do not look into java.* classes. Should technically be filtered by above, but when that's
264     // configurable have this.
265     if (strncmp("java", name, 4) == 0) {
266         return;
267     }
268 
269     // Do not look into our Java classes.
270     if (strncmp("com/android/lock_checker", name, 24) == 0) {
271         return;
272     }
273 
274     class JvmtiAllocator: public dex::Writer::Allocator {
275     public:
276         explicit JvmtiAllocator(::jvmtiEnv* jvmti) :
277                 jvmti_(jvmti) {
278         }
279 
280         void* Allocate(size_t size) override {
281             unsigned char* res = nullptr;
282             jvmti_->Allocate(size, &res);
283             return res;
284         }
285 
286         void Free(void* ptr) override {
287             jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr));
288         }
289 
290     private:
291         ::jvmtiEnv* jvmti_;
292     };
293     JvmtiAllocator allocator(jvmtiEnv);
294     std::pair<dex::u1*, size_t> result = maybeTransform(name, classDataLen, classData,
295             &allocator);
296 
297     if (result.second > 0) {
298         *newClassData = result.first;
299         *newClassDataLen = static_cast<jint>(result.second);
300     }
301 }
302 
dataDumpRequestHook(jvmtiEnv * jvmtiEnv ATTRIBUTE_UNUSED)303 void dataDumpRequestHook(jvmtiEnv* jvmtiEnv ATTRIBUTE_UNUSED) {
304     if (gJavaVM == nullptr) {
305         LOG(ERROR) << "No JavaVM for dump";
306         return;
307     }
308     JNIEnv* env;
309     if (gJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
310         LOG(ERROR) << "Could not get env for dump";
311         return;
312     }
313     jclass lockHookClass = env->FindClass("com/android/lock_checker/LockHook");
314     if (lockHookClass == nullptr) {
315         env->ExceptionClear();
316         LOG(ERROR) << "Could not find LockHook class";
317         return;
318     }
319     jmethodID dumpId = env->GetStaticMethodID(lockHookClass, "dump", "()V");
320     if (dumpId == nullptr) {
321         env->ExceptionClear();
322         LOG(ERROR) << "Could not find LockHook.dump";
323         return;
324     }
325     env->CallStaticVoidMethod(lockHookClass, dumpId);
326     env->ExceptionClear();
327 }
328 
329 // A function for dladdr to search.
lock_agent_tag_fn()330 extern "C" __attribute__ ((visibility ("default"))) void lock_agent_tag_fn() {
331 }
332 
fileExists(const std::string & path)333 bool fileExists(const std::string& path) {
334     struct stat statBuf;
335     int rc = stat(path.c_str(), &statBuf);
336     return rc == 0;
337 }
338 
findLockAgentJar()339 std::string findLockAgentJar() {
340     // Check whether the jar is located next to the agent's so.
341 #ifndef __APPLE__
342     {
343         Dl_info info;
344         if (dladdr(reinterpret_cast<const void*>(&lock_agent_tag_fn), /* out */ &info) != 0) {
345             std::string lockAgentSoPath = info.dli_fname;
346             std::string dir = android::base::Dirname(lockAgentSoPath);
347             std::string lockAgentJarPath = dir + "/" + "lockagent.jar";
348             if (fileExists(lockAgentJarPath)) {
349                 return lockAgentJarPath;
350             }
351         } else {
352             LOG(ERROR) << "dladdr failed";
353         }
354     }
355 #endif
356 
357     std::string sysFrameworkPath = "/system/framework/lockagent.jar";
358     if (fileExists(sysFrameworkPath)) {
359         return sysFrameworkPath;
360     }
361 
362     std::string relPath = "lockagent.jar";
363     if (fileExists(relPath)) {
364         return relPath;
365     }
366 
367     return "";
368 }
369 
prepareHook(jvmtiEnv * env)370 void prepareHook(jvmtiEnv* env) {
371     // Inject the agent Java code.
372     {
373         std::string path = findLockAgentJar();
374         if (path.empty()) {
375             LOG(FATAL) << "Could not find lockagent.jar";
376         }
377         LOG(INFO) << "Will load Java parts from " << path;
378         jvmtiError res = env->AddToBootstrapClassLoaderSearch(path.c_str());
379         if (res != JVMTI_ERROR_NONE) {
380             LOG(FATAL) << "Could not add lockagent from " << path << " to boot classpath: " << res;
381         }
382     }
383 
384     jvmtiCapabilities caps;
385     memset(&caps, 0, sizeof(caps));
386     caps.can_retransform_classes = 1;
387 
388     if (env->AddCapabilities(&caps) != JVMTI_ERROR_NONE) {
389         LOG(FATAL) << "Could not add caps";
390     }
391 
392     jvmtiEventCallbacks cb;
393     memset(&cb, 0, sizeof(cb));
394     cb.ClassFileLoadHook = transformHook;
395     cb.DataDumpRequest = dataDumpRequestHook;
396 
397     if (env->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
398         LOG(FATAL) << "Could not set cb";
399     }
400 
401     if (env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, nullptr)
402             != JVMTI_ERROR_NONE) {
403         LOG(FATAL) << "Could not enable events";
404     }
405     if (env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr)
406             != JVMTI_ERROR_NONE) {
407         LOG(FATAL) << "Could not enable events";
408     }
409 }
410 
attach(JavaVM * vm,char * options,void * reserved ATTRIBUTE_UNUSED)411 jint attach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
412     gJavaVM = vm;
413 
414     jvmtiEnv* env;
415     jint jvmError = vm->GetEnv(reinterpret_cast<void**>(&env), JVMTI_VERSION_1_2);
416     if (jvmError != JNI_OK) {
417         return jvmError;
418     }
419 
420     prepareHook(env);
421 
422     std::vector<std::string> config = android::base::Split(options, ",");
423     for (const std::string& c : config) {
424         if (c == "native_crash") {
425             gForkCrash = true;
426         } else if (c == "java_crash") {
427             gJavaCrash = true;
428         }
429     }
430 
431     return JVMTI_ERROR_NONE;
432 }
433 
434 extern "C" JNIEXPORT
Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv *,jclass)435 jboolean JNICALL Java_com_android_lock_1checker_LockHook_getNativeHandlingConfig(JNIEnv*, jclass) {
436     return gForkCrash ? JNI_TRUE : JNI_FALSE;
437 }
438 
439 extern "C" JNIEXPORT jboolean JNICALL
Java_com_android_lock_1checker_LockHook_getSimulateCrashConfig(JNIEnv *,jclass)440 Java_com_android_lock_1checker_LockHook_getSimulateCrashConfig(JNIEnv*, jclass) {
441     return gJavaCrash ? JNI_TRUE : JNI_FALSE;
442 }
443 
Java_com_android_lock_1checker_LockHook_nWtf(JNIEnv * env,jclass,jstring msg)444 extern "C" JNIEXPORT void JNICALL Java_com_android_lock_1checker_LockHook_nWtf(JNIEnv* env, jclass,
445         jstring msg) {
446     if (!gForkCrash || msg == nullptr) {
447         return;
448     }
449 
450     // Create a native crash with the given message. Decouple from the current crash to create a
451     // tombstone but continue on.
452     //
453     // TODO: Once there are not so many reports, consider making this fatal for the calling process.
454     ScopedUtfChars utf(env, msg);
455     if (utf.c_str() == nullptr) {
456         return;
457     }
458     const char* args[] = {
459         "/system/bin/lockagent_crasher",
460         utf.c_str(),
461         nullptr
462     };
463     pid_t pid = fork();
464     if (pid < 0) {
465         return;
466     }
467     if (pid == 0) {
468         // Double fork so we return quickly. Leave init to deal with the zombie.
469         pid_t pid2 = fork();
470         if (pid2 == 0) {
471             execv(args[0], const_cast<char* const*>(args));
472             _exit(1);
473             __builtin_unreachable();
474         }
475         _exit(0);
476         __builtin_unreachable();
477     }
478     int status;
479     waitpid(pid, &status, 0);  // Ignore any results.
480 }
481 
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)482 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
483     return attach(vm, options, reserved);
484 }
485 
Agent_OnLoad(JavaVM * vm,char * options,void * reserved)486 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
487     return attach(vm, options, reserved);
488 }
489 
locktest_main(int argc,char * argv[])490 int locktest_main(int argc, char *argv[]) {
491     if (argc != 3) {
492         LOG(FATAL) << "Need two arguments: dex-file class-name";
493     }
494     struct stat statBuf;
495     int rc = stat(argv[1], &statBuf);
496     if (rc != 0) {
497         PLOG(FATAL) << "Could not get file size for " << argv[1];
498     }
499     std::unique_ptr<char[]> data(new char[statBuf.st_size]);
500     {
501         android::base::unique_fd fd(open(argv[1], O_RDONLY));
502         if (fd.get() == -1) {
503             PLOG(FATAL) << "Could not open file " << argv[1];
504         }
505         if (!android::base::ReadFully(fd.get(), data.get(), statBuf.st_size)) {
506             PLOG(FATAL) << "Could not read file " << argv[1];
507         }
508     }
509 
510     class NewDeleteAllocator: public dex::Writer::Allocator {
511     public:
512         explicit NewDeleteAllocator() {
513         }
514 
515         void* Allocate(size_t size) override {
516             return new char[size];
517         }
518 
519         void Free(void* ptr) override {
520             delete[] reinterpret_cast<char*>(ptr);
521         }
522     };
523     NewDeleteAllocator allocator;
524 
525     std::pair<dex::u1*, size_t> result = maybeTransform(argv[2], statBuf.st_size,
526             reinterpret_cast<unsigned char*>(data.get()), &allocator);
527 
528     if (result.second == 0) {
529         LOG(INFO) << "No transformation";
530         return 0;
531     }
532 
533     std::string newName(argv[1]);
534     newName.append(".new");
535 
536     {
537         android::base::unique_fd fd(
538                 open(newName.c_str(), O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR));
539         if (fd.get() == -1) {
540             PLOG(FATAL) << "Could not open file " << newName;
541         }
542         if (!android::base::WriteFully(fd.get(), result.first, result.second)) {
543             PLOG(FATAL) << "Could not write file " << newName;
544         }
545     }
546     LOG(INFO) << "Transformed file written to " << newName;
547 
548     return 0;
549 }
550 
551 }  // namespace
552 
main(int argc,char * argv[])553 int main(int argc, char *argv[]) {
554     return locktest_main(argc, argv);
555 }
556