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