/* Code called from dvmJniStartup() * Initializes the gPolicyTable for fast lookup of taint policy * profiles for methods. */ void dvmTaintPropJniStartup() { TaintPolicy* policy; u4 hash; /* Create the policy table (perfect size) */ gPolicyTable = dvmHashTableCreate( dvmHashSize(TAINT_POLICY_TABLE_SIZE), freeTaintPolicy); for (policy = gDvmJniTaintPolicy; policy->classDescriptor != NULL; policy++) { const TaintProfile *profile; /* Create the method table for this class */ policy->methodTable = dvmHashTableCreate( TAINT_PROFILE_TABLE_SIZE, freeTaintProfile); /* Add all of the methods */ for (profile = &policy->profiles[0]; profile->methodName != NULL; profile++) { hash = dvmComputeUtf8Hash(profile->methodName); dvmHashTableLookup(policy->methodTable, hash,(void *) profile, hashcmpTaintProfile, true); } /* Add this class to gPolicyTable */ hash = dvmComputeUtf8Hash(policy->classDescriptor); dvmHashTableLookup(gPolicyTable, hash, policy, hashcmpTaintPolicy, true); } #ifdef TAINT_JNI_LOG /* JNI logging for debugging purposes */ gJniLogSeen = dvmHashTableCreate(dvmHashSize(50), free); #endif }
/* Returns the TaintPolicyProfile associated with this method * - returns NULL if not found */ TaintProfile* getPolicyProfile(const Method* method) { TaintPolicy* policy = NULL; TaintProfile* profile = NULL; u4 hash; /* temporary variables for the search */ TaintPolicy tPol = {NULL, NULL, NULL}; TaintProfile tProf = {NULL, NULL}; dvmHashTableLock(gPolicyTable); /* Find the class */ hash = dvmComputeUtf8Hash(method->clazz->descriptor); tPol.classDescriptor = method->clazz->descriptor; policy = (TaintPolicy*) dvmHashTableLookup(gPolicyTable, hash, &tPol, hashcmpTaintPolicy, false); if (policy != NULL) { dvmHashTableLock(policy->methodTable); /* Find the Method */ hash = dvmComputeUtf8Hash(method->name); tProf.methodName = method->name; profile = (TaintProfile*) dvmHashTableLookup(policy->methodTable, hash, &tProf, hashcmpTaintProfile, false); dvmHashTableUnlock(policy->methodTable); } dvmHashTableUnlock(gPolicyTable); return profile; }
/* * 添加特定的DexOrJar到hash表中 */ static void addToDexFileTable(DexOrJar* pDexOrJar) { /* * Later on, we will receive this pointer as an argument and need * to find it in the hash table without knowing if it's valid or * not, which means we can't compute a hash value from anything * inside DexOrJar. We don't share DexOrJar structs when the same * file is opened multiple times, so we can just use the low 32 * bits of the pointer as the hash. */ u4 hash = (u4) pDexOrJar; void* result; dvmHashTableLock(gDvm.userDexFiles); /* true 代表添加 false 代表不添加*/ result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, true); dvmHashTableUnlock(gDvm.userDexFiles); if (result != pDexOrJar) { ALOGE("Pointer has already been added?"); dvmAbort(); } pDexOrJar->okayToFree = true; }
/* *查询hprof class id. */ hprof_class_object_id hprofLookupClassId(const ClassObject *clazz) { void *val; if (clazz == NULL) { /* Someone's probably looking up the superclass * of java.lang.Object or of a primitive class. */ return (hprof_class_object_id)0; } dvmHashTableLock(gClassHashTable); /* We're using the hash table as a list. * TODO: replace the hash table with a more suitable structure */ val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz), (void *)clazz, classCmp, true); assert(val != NULL); dvmHashTableUnlock(gClassHashTable); /* Make sure that the class's name is in the string table. * This is a bunch of extra work that we only have to do * because of the order of tables in the output file * (strings need to be dumped before classes). */ getPrettyClassNameId(clazz->descriptor); return (hprof_class_object_id)clazz; }
static StringObject* insertString(HashTable* table, u4 key, StringObject* value) { if (dvmIsNonMovingObject(value) == false) { value = (StringObject*)dvmCloneObject(value, ALLOC_NON_MOVING); } void* entry = dvmHashTableLookup(table, key, (void*)value, dvmHashcmpStrings, true); return (StringObject*)entry; }
static Thread* lookupThreadInHash(u4 threadId) { Thread* result; dvmHashTableLock(gDvm.offThreadTable); { // This is a bit of a hack but whatever, it gets the job done without having // to allocate an entire Thread structure. result = (Thread*)dvmHashTableLookup(gDvm.offThreadTable, threadId, ((char*)&threadId) - offsetof(Thread, threadId), compareThreadData, false); } dvmHashTableUnlock(gDvm.offThreadTable); return result; }
static u4 hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace) { StackTraceEntry *val; u4 hashValue; int serial; /* * Create the hash table on first contact. We can't do this in * hprofStartupStack, because we have to compute stack trace * serial numbers and place them into object headers before the * rest of hprof is triggered by a GC event. */ if (gStackTraceHashTable == NULL) { gStackTraceHashTable = dvmHashTableCreate(512, free); } dvmHashTableLock(gStackTraceHashTable); hashValue = computeStackTraceHash(stackTrace); val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace, (HashCompareFunc)stackCmp, false); if (val == NULL) { StackTraceEntry *newStackTrace; newStackTrace = stackDup(stackTrace); newStackTrace->trace.serialNumber = ++gSerialNumber; val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)newStackTrace, (HashCompareFunc)stackCmp, true); assert(val != NULL); } /* Mark the trace as live (in use by an object in the current heap). */ val->live = 1; /* Grab the serial number before unlocking the table. */ serial = val->trace.serialNumber; dvmHashTableUnlock(gStackTraceHashTable); return serial; }
hprof_stack_frame_id hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry) { StackFrameEntry *val; u4 hashValue; /* * Create the hash table on first contact. We can't do this in * hprofStartupStackFrame, because we have to compute stack trace * serial numbers and place them into object headers before the * rest of hprof is triggered by a GC event. */ if (gStackFrameHashTable == NULL) { gStackFrameHashTable = dvmHashTableCreate(512, free); } dvmHashTableLock(gStackFrameHashTable); hashValue = computeStackFrameHash(stackFrameEntry); val = dvmHashTableLookup(gStackFrameHashTable, hashValue, (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false); if (val == NULL) { const StackFrameEntry *newStackFrameEntry; newStackFrameEntry = stackFrameDup(stackFrameEntry); val = dvmHashTableLookup(gStackFrameHashTable, hashValue, (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true); assert(val != NULL); } /* Mark the frame as live (in use by an object in the current heap). */ val->live = 1; dvmHashTableUnlock(gStackFrameHashTable); return (hprof_stack_frame_id) val; }
/* * Verify that the "cookie" is a DEX file we opened. * * Expects that the hash table will be *unlocked* here. * * If the cookie is invalid, we throw an exception and return "false". */ static bool validateCookie(int cookie) { DexOrJar* pDexOrJar = (DexOrJar*) cookie; LOGVV("+++ dex verifying cookie %p", pDexOrJar); if (pDexOrJar == NULL) return false; u4 hash = cookie; dvmHashTableLock(gDvm.userDexFiles); void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, false); dvmHashTableUnlock(gDvm.userDexFiles); if (result == NULL) { dvmThrowRuntimeException("invalid DexFile cookie"); return false; } return true; }
/* * Verify that the "cookie" is a DEX file we opened. * * Expects that the hash table will be *unlocked* here. * * If the cookie is invalid, we throw an exception and return "false". */ static bool validateCookie(int cookie) { DexOrJar* pDexOrJar = (DexOrJar*) cookie; LOGVV("+++ dex verifying cookie %p\n", pDexOrJar); if (pDexOrJar == NULL) return false; u4 hash = dvmComputeUtf8Hash(pDexOrJar->fileName); dvmHashTableLock(gDvm.userDexFiles); void* result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, false); dvmHashTableUnlock(gDvm.userDexFiles); if (result == NULL) { dvmThrowException("Ljava/lang/RuntimeException;", "invalid DexFile cookie"); return false; } return true; }
/* Used to propagate taint for JNI methods * Two types of propagation: * 1) simple conservative propagation based on parameters * 2) propagation based on function profile policies */ void dvmTaintPropJniMethod(const u4* args, JValue* pResult, const Method* method, const int nativeTaint) { const DexProto* proto = &method->prototype; DexParameterIterator pIterator; int nParams = dexProtoGetParameterCount(proto); int pStart = (dvmIsStaticMethod(method)?0:1); /* index where params start */ /* Consider 3 arguments. [x] indicates return taint index * 0 1 2 [3] 4 5 6 */ int nArgs = method->insSize; u4* rtaint = (u4*) &args[nArgs]; /* The return taint value */ int tStart = nArgs+1; /* index of args[] where taint values start */ int tEnd = nArgs*2; /* index of args[] where taint values end */ u4 tag = TAINT_CLEAR; int i; #if 0 { char *desc = dexProtoCopyMethodDescriptor(proto); ALOGW("Jni: %s.%s%s, descriptor: %s", method->clazz->descriptor, method->name, dvmIsStaticMethod(method)?"[static]":"", desc); free(desc); } #endif #ifdef TAINT_JNI_LOG /* JNI logging for debugging purposes */ if (gJniLog) { u4 hash; int len; char *inStr, *outStr; len = strlen(method->clazz->descriptor) + 1 + strlen(method->name); inStr = (char*) malloc(len+1); strcpy(inStr, method->clazz->descriptor); strcat(inStr, "."); strcat(inStr, method->name); hash = dvmComputeUtf8Hash(inStr); dvmHashTableLock(gJniLogSeen); outStr = (char*) dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, false); if (outStr == NULL) { /* New method! */ /* add it */ dvmHashTableLookup(gJniLogSeen, hash, inStr, (HashCompareFunc) strcmp, true); } else { free(inStr); /* don't need this anymore */ } dvmHashTableUnlock(gJniLogSeen); } #endif /* Union the taint tags, this includes object ref tags * - we don't need to worry about static vs. not static, because getting * the taint tag on the "this" object reference is a good * - we don't need to worry about wide registers, because the stack * interleaving of taint tags makes it transparent */ /*for (i = tStart; i <= tEnd; i++) { tag |= args[i]; if (args[i]!=0 && args[i]!=TAINT_ACCELEROMETER) //found a taint, and its not the accelerometer spam either ALOGD("dvmTaintPropJNIMethod found taint %08x in method=%s:%s(%s) for arg[%d]=%08x", args[i], method->clazz->descriptor, method->name, method->shorty, i-nArgs, args[i-nArgs]); }*/ /* If not static, pull any taint from the "this" object */ /*if (!dvmIsStaticMethod(method)) { tag |= getObjectTaint((Object*)args[0], method->clazz->descriptor); }*/ /* Union taint from Objects we care about */ dexParameterIteratorInit(&pIterator, proto); for (i=pStart; ; i++) { const char* desc = dexParameterIteratorNextDescriptor(&pIterator); if (desc == NULL) { break; } if (desc[0] == '[' || desc[0] == 'L') { tag |= getObjectTaint((Object*) args[i], desc); } if (desc[0] == 'J' || desc[0] == 'D') { /* wide argument, increment index one more */ i++; } } /* Look at the taint policy profiles (may have return taint) */ //tag |= propMethodProfile(args, method); /* Update return taint according to the return type */ //Native Tainting: get taint Value from native code //if (tag) { if (nativeTaint) { const char* desc = dexProtoGetReturnType(proto); //setReturnTaint(tag, rtaint, pResult, desc); ALOGD("dvmTaintPropJniMethod: setting result taint to %x", nativeTaint); setReturnTaint(nativeTaint, rtaint, pResult, desc); } }
static void addThreadToHash(Thread* thread) { dvmHashTableLock(gDvm.offThreadTable); { dvmHashTableLookup(gDvm.offThreadTable, thread->threadId, thread, compareThreadData, true); } dvmHashTableUnlock(gDvm.offThreadTable); }
static StringObject* lookupString(HashTable* table, u4 key, StringObject* value) { void* entry = dvmHashTableLookup(table, key, (void*)value, dvmHashcmpStrings, false); return (StringObject*)entry; }
/* * Some quick hash table tests. */ bool dvmTestHash() { HashTable* pTab; char tmpStr[64]; const char* str; u4 hash; int i; ALOGV("TestHash BEGIN"); pTab = dvmHashTableCreate(dvmHashSize(12), free); if (pTab == NULL) return false; dvmHashTableLock(pTab); /* add some entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); } dvmHashTableUnlock(pTab); /* make sure we can find all entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { ALOGE("TestHash: failure: could not find '%s'", tmpStr); /* return false */ } } /* make sure it behaves correctly when entry not found and !doAdd */ sprintf(tmpStr, "entry %d", 17); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { /* good */ } else { ALOGE("TestHash found nonexistent string (improper add?)"); } dumpForeach(pTab); dumpIterator(pTab); /* make sure they all get freed */ dvmHashTableFree(pTab); /* * Round 2: verify probing & tombstones. */ pTab = dvmHashTableCreate(dvmHashSize(2), free); if (pTab == NULL) return false; hash = 0; /* two entries, same hash, different values */ const char* str1; str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"), (HashCompareFunc) strcmp, true); assert(str1 != NULL); str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"), (HashCompareFunc) strcmp, true); /* remove the first one */ if (!dvmHashTableRemove(pTab, hash, (void*)str1)) ALOGE("TestHash failed to delete item"); else free((void*)str1); // "Remove" doesn't call the free func /* make sure iterator doesn't included deleted entries */ int count = 0; HashIter iter; for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { count++; } if (count != 1) { ALOGE("TestHash wrong number of entries (%d)", count); } /* see if we can find them */ str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"one", (HashCompareFunc) strcmp,false); if (str != NULL) ALOGE("TestHash deleted entry has returned!"); str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"two", (HashCompareFunc) strcmp,false); if (str == NULL) ALOGE("TestHash entry vanished"); /* force a table realloc to exercise tombstone removal */ for (i = 0; i < 20; i++) { sprintf(tmpStr, "entry %d", i); str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); assert(str != NULL); } dvmHashTableFree(pTab); ALOGV("TestHash END"); return true; }
/* * private static int openDexFile(String sourceName, String outputName, * int flags) throws IOException * * Open a DEX file, returning a pointer to our internal data structure. * * "sourceName" should point to the "source" jar or DEX file. * * If "outputName" is NULL, the DEX code will automatically find the * "optimized" version in the cache directory, creating it if necessary. * If it's non-NULL, the specified file will be used instead. * * TODO: at present we will happily open the same file more than once. * To optimize this away we could search for existing entries in the hash * table and refCount them. Requires atomic ops or adding "synchronized" * to the non-native code that calls here. */ static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult) { StringObject* sourceNameObj = (StringObject*) args[0]; StringObject* outputNameObj = (StringObject*) args[1]; int flags = args[2]; DexOrJar* pDexOrJar = NULL; JarFile* pJarFile; RawDexFile* pRawDexFile; char* sourceName; char* outputName; if (sourceNameObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", NULL); RETURN_VOID(); } sourceName = dvmCreateCstrFromString(sourceNameObj); if (outputNameObj != NULL) outputName = dvmCreateCstrFromString(outputNameObj); else outputName = NULL; /* * We have to deal with the possibility that somebody might try to * open one of our bootstrap class DEX files. The set of dependencies * will be different, and hence the results of optimization might be * different, which means we'd actually need to have two versions of * the optimized DEX: one that only knows about part of the boot class * path, and one that knows about everything in it. The latter might * optimize field/method accesses based on a class that appeared later * in the class path. * * We can't let the user-defined class loader open it and start using * the classes, since the optimized form of the code skips some of * the method and field resolution that we would ordinarily do, and * we'd have the wrong semantics. * * We have to reject attempts to manually open a DEX file from the boot * class path. The easiest way to do this is by filename, which works * out because variations in name (e.g. "/system/framework/./ext.jar") * result in us hitting a different dalvik-cache entry. It's also fine * if the caller specifies their own output file. */ if (dvmClassPathContains(gDvm.bootClassPath, sourceName)) { LOGW("Refusing to reopen boot DEX '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "Re-opening BOOTCLASSPATH DEX files is not allowed"); free(sourceName); RETURN_VOID(); } /* * Try to open it directly as a DEX. If that fails, try it as a Zip * with a "classes.dex" inside. */ if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) { LOGV("Opening DEX file '%s' (DEX)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = true; pDexOrJar->pRawDexFile = pRawDexFile; } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) { LOGV("Opening DEX file '%s' (Jar)\n", sourceName); pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar)); pDexOrJar->isDex = false; pDexOrJar->pJarFile = pJarFile; } else { LOGV("Unable to open DEX file '%s'\n", sourceName); dvmThrowException("Ljava/io/IOException;", "unable to open DEX file"); } if (pDexOrJar != NULL) { pDexOrJar->fileName = sourceName; /* add to hash table */ u4 hash = dvmComputeUtf8Hash(sourceName); void* result; dvmHashTableLock(gDvm.userDexFiles); result = dvmHashTableLookup(gDvm.userDexFiles, hash, pDexOrJar, hashcmpDexOrJar, true); dvmHashTableUnlock(gDvm.userDexFiles); if (result != pDexOrJar) { LOGE("Pointer has already been added?\n"); dvmAbort(); } pDexOrJar->okayToFree = true; } else free(sourceName); RETURN_PTR(pDexOrJar); }