/* Returns the object pointer for a field * - obj only used if the field is not static * - Note: will not return an array object */ Object* getObjectFromField(Field* field, Object* obj) { if (field->signature[0] != 'L') { return NULL; } if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*) field; return dvmGetStaticFieldObject(sfield); } else { InstField* ifield = (InstField*) field; return dvmGetFieldObject(obj, ifield->byteOffset); } }
/* * Convert a field pointer to a slot number. * * We use positive values starting from 0 for instance fields, negative * values starting from -1 for static fields. */ static int fieldToSlot(const Field* field, const ClassObject* clazz) { int slot; if (dvmIsStaticField(field)) { slot = (StaticField*)field - &clazz->sfields[0]; assert(slot >= 0 && slot < clazz->sfieldCount); slot = -(slot+1); } else { slot = (InstField*)field - clazz->ifields; assert(slot >= 0 && slot < clazz->ifieldCount); } return slot; }
/* add tag to a field * - obj only used if the field is not static */ void addTaintToField(Field* field, Object* obj, u4 tag) { if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*) field; tag |= dvmGetStaticFieldTaint(sfield); dvmSetStaticFieldTaint(sfield, tag); } else { InstField* ifield = (InstField*) field; if (field->signature[0] == 'J' || field->signature[0] == 'D') { tag |= dvmGetFieldTaintWide(obj, ifield->byteOffset); dvmSetFieldTaintWide(obj, ifield->byteOffset, tag); } else { tag |= dvmGetFieldTaint(obj, ifield->byteOffset); dvmSetFieldTaint(obj, ifield->byteOffset, tag); } } }
/* Returns the taint tag for a field * - obj only used if the field is not static */ u4 getTaintFromField(Field* field, Object* obj) { u4 tag = TAINT_CLEAR; if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*) field; tag = dvmGetStaticFieldTaint(sfield); } else { InstField* ifield = (InstField*) field; if (field->signature[0] == 'J' || field->signature[0] == 'D') { tag = dvmGetFieldTaintWide(obj, ifield->byteOffset); } else { tag = dvmGetFieldTaint(obj, ifield->byteOffset); } } return tag; }
/* * Alternate version of dvmResolveStaticField(). * * Does not force initialization of the resolved field's class. * * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; StaticField* resField; resField = (StaticField*)dvmDexGetResolvedField(pDvmDex, sfieldIdx); if (resField == NULL) { const DexFieldId* pFieldId; ClassObject* resClass; pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); /* * Find the field's class. */ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); if (resClass == NULL) { //dvmClearOptException(dvmThreadSelf()); assert(!dvmCheckException(dvmThreadSelf())); if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } const char* fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx); resField = (StaticField*)dvmFindFieldHier(resClass, fieldName, dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { ALOGD("DexOpt: couldn't find static field %s.%s", resClass->descriptor, fieldName); if (pFailure != NULL) *pFailure = VERIFY_ERROR_NO_FIELD; return NULL; } if (!dvmIsStaticField(resField)) { ALOGD("DexOpt: wanted static, got instance for field %s.%s", resClass->descriptor, fieldName); if (pFailure != NULL) *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } /* * Add it to the resolved table so we're faster on the next lookup. * * We can only do this if we're in "dexopt", because the presence * of a valid value in the resolution table implies that the class * containing the static field has been initialized. */ if (gDvm.optimizing) dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); } /* access allowed? */ tweakLoader(referrer, resField->clazz); bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); untweakLoader(referrer, resField->clazz); if (!allowed) { ALOGI("DexOpt: access denied from %s to field %s.%s", referrer->descriptor, resField->clazz->descriptor, resField->name); if (pFailure != NULL) *pFailure = VERIFY_ERROR_ACCESS_FIELD; return NULL; } return resField; }
/* * Get the address of a field from an object. This can be used with "get" * or "set". * * "declaringClass" is the class in which the field was declared. For an * instance field, "obj" is the object that holds the field data; for a * static field its value is ignored. * * "If the underlying field is static, the class that declared the * field is initialized if it has not already been initialized." * * On failure, throws an exception and returns NULL. * * The documentation lists exceptional conditions and the exceptions that * should be thrown, but doesn't say which exception previals when two or * more exceptional conditions exist at the same time. For example, * attempting to set a protected field from an unrelated class causes an * IllegalAccessException, while passing in a data type that doesn't match * the field causes an IllegalArgumentException. If code does both at the * same time, we have to choose one or othe other. * * The expected order is: * (1) Check for illegal access. Throw IllegalAccessException. * (2) Make sure the object actually has the field. Throw * IllegalArgumentException. * (3) Make sure the field matches the expected type, e.g. if we issued * a "getInteger" call make sure the field is an integer or can be * converted to an int with a widening conversion. Throw * IllegalArgumentException. * (4) Make sure "obj" is not null. Throw NullPointerException. * * TODO: we're currently handling #3 after #4, because we don't check the * widening conversion until we're actually extracting the value from the * object (which won't work well if it's a null reference). */ static JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass, int slot, bool isSetOperation, bool noAccessCheck) { Field* field; JValue* result; field = dvmSlotToField(declaringClass, slot); assert(field != NULL); /* verify access */ if (!noAccessCheck) { if (isSetOperation && dvmIsFinalField(field)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "field is marked 'final'"); return NULL; } ClassObject* callerClass = dvmGetCaller2Class(dvmThreadSelf()->curFrame); /* * We need to check two things: * (1) Would an instance of the calling class have access to the field? * (2) If the field is "protected", is the object an instance of the * calling class, or is the field's declaring class in the same * package as the calling class? * * #1 is basic access control. #2 ensures that, just because * you're a subclass of Foo, you can't mess with protected fields * in arbitrary Foo objects from other packages. */ if (!dvmCheckFieldAccess(callerClass, field)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "access to field not allowed"); return NULL; } if (dvmIsProtectedField(field)) { bool isInstance, samePackage; if (obj != NULL) isInstance = dvmInstanceof(obj->clazz, callerClass); else isInstance = false; samePackage = dvmInSamePackage(declaringClass, callerClass); if (!isInstance && !samePackage) { dvmThrowException("Ljava/lang/IllegalAccessException;", "access to protected field not allowed"); return NULL; } } } if (dvmIsStaticField(field)) { /* init class if necessary, then return ptr to storage in "field" */ if (!dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } } result = dvmStaticFieldPtr((StaticField*) field); } else { /* * Verify object is of correct type (i.e. it actually has the * expected field in it), then grab a pointer to obj storage. * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL. */ if (!dvmVerifyObjectInClass(obj, declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); if (obj != NULL) { LOGD("Wrong type of object for field lookup: %s %s\n", obj->clazz->descriptor, declaringClass->descriptor); } return NULL; } result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset); } return result; }
/* * Alternate version of dvmResolveInstField(). * * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; InstField* resField; resField = (InstField*) dvmDexGetResolvedField(pDvmDex, ifieldIdx); if (resField == NULL) { const DexFieldId* pFieldId; ClassObject* resClass; pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); if (resClass == NULL) { //dvmClearOptException(dvmThreadSelf()); assert(!dvmCheckException(dvmThreadSelf())); if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } resField = (InstField*)dvmFindFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { ALOGD("DexOpt: couldn't find field %s.%s", resClass->descriptor, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); if (pFailure != NULL) *pFailure = VERIFY_ERROR_NO_FIELD; return NULL; } if (dvmIsStaticField(resField)) { ALOGD("DexOpt: wanted instance, got static for field %s.%s", resClass->descriptor, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); if (pFailure != NULL) *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } /* * Add it to the resolved table so we're faster on the next lookup. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*) resField); } /* access allowed? */ tweakLoader(referrer, resField->clazz); bool allowed = dvmCheckFieldAccess(referrer, (Field*)resField); untweakLoader(referrer, resField->clazz); if (!allowed) { ALOGI("DexOpt: access denied from %s to field %s.%s", referrer->descriptor, resField->clazz->descriptor, resField->name); if (pFailure != NULL) *pFailure = VERIFY_ERROR_ACCESS_FIELD; return NULL; } return resField; }
/* * Primitive field setters, e.g.: * private void setIField(Object o, Class declaringClass, * Class type, int slot, boolean noAccessCheck, int type_no, int value) * * The "type_no" is defined by the java.lang.reflect.Field class. */ static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* obj = (Object*) args[1]; ClassObject* declaringClass = (ClassObject*) args[2]; ClassObject* fieldType = (ClassObject*) args[3]; int slot = args[4]; bool noAccessCheck = (args[5] != 0); int typeNum = args[6]; const s4* valuePtr = (s4*) &args[7]; #ifdef WITH_TAINT_TRACKING /* rtaint = args[8] * thisPtr taint = args[9] * obj taint = args[10] * declaringClass taint = args[11] * fieldType taint = args[12] * slot taint = args[13] * noAccessCheck taint = args[14] * typeNum taint = args[15] */ u4 valueTaint = args[16]; #endif PrimitiveType srcType = convPrimType(typeNum); JValue* fieldPtr; JValue value; if (!dvmIsPrimitiveClass(fieldType)) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "not a primitive field"); RETURN_VOID(); } /* convert the 32/64-bit arg to a JValue matching the field type */ if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType, valuePtr, &(value.i)) < 0) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "invalid primitive conversion"); RETURN_VOID(); } /* get a pointer to the field's data; performs access checks */ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck); if (fieldPtr == NULL) RETURN_VOID(); /* store 4 or 8 bytes */ if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { fieldPtr->j = value.j; } else { fieldPtr->i = value.i; } #ifdef WITH_TAINT_TRACKING /* If we got this far, we know the fields is okay to access and there * will not be a problem getting the field from the slot */ { Field* field = dvmSlotToField(declaringClass, slot); assert(field != NULL); if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*)field; dvmSetStaticFieldTaint(sfield, valueTaint); } else { /* Note, getFieldDataAddr() already checked that * obj is of type declaringClass, so no need to check here */ InstField* ifield = (InstField*)field; if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { dvmSetFieldTaintWide(obj, ifield->byteOffset, valueTaint); } else { dvmSetFieldTaint(obj, ifield->byteOffset, valueTaint); } } } #endif RETURN_VOID(); }
/* * Primitive field getters, e.g.: * private double getIField(Object o, Class declaringClass, * Class type, int slot, boolean noAccessCheck, int type_no) * * The "type_no" is defined by the java.lang.reflect.Field class. */ static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* obj = (Object*) args[1]; ClassObject* declaringClass = (ClassObject*) args[2]; ClassObject* fieldType = (ClassObject*) args[3]; int slot = args[4]; bool noAccessCheck = (args[5] != 0); int typeNum = args[6]; #ifdef WITH_TAINT_TRACKING u4* rtaint = (u4*) &args[7]; /* return taint tag slot */ #endif PrimitiveType targetType = convPrimType(typeNum); const JValue* fieldPtr; JValue value; if (!dvmIsPrimitiveClass(fieldType)) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "not a primitive field"); RETURN_VOID(); } /* get a pointer to the field's data; performs access checks */ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck); if (fieldPtr == NULL) RETURN_VOID(); /* copy 4 or 8 bytes out */ if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { value.j = fieldPtr->j; } else { value.i = fieldPtr->i; } /* retrieve value, performing a widening conversion if necessary */ if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType, &(value.i), &(pResult->i)) < 0) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "invalid primitive conversion"); RETURN_VOID(); } #ifdef WITH_TAINT_TRACKING /* If we got this far, we know the fields is okay to access and there * will not be a problem getting the field from the slot */ { Field* field = dvmSlotToField(declaringClass, slot); assert(field != NULL); if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*)field; *rtaint = dvmGetStaticFieldTaint(sfield); } else { /* Note, getFieldDataAddr() already checked that * obj is of type declaringClass, so no need to check here */ InstField* ifield = (InstField*)field; if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { *rtaint = dvmGetFieldTaintWide(obj, ifield->byteOffset); } else { *rtaint = dvmGetFieldTaint(obj, ifield->byteOffset); } } } #endif }
/* * private void setField(Object o, Class declaringClass, Class type, * int slot, boolean noAccessCheck, Object value) * * When assigning into a primitive field we will automatically extract * the value from box types. */ static void Dalvik_java_lang_reflect_Field_setField(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* obj = (Object*) args[1]; ClassObject* declaringClass = (ClassObject*) args[2]; ClassObject* fieldType = (ClassObject*) args[3]; int slot = args[4]; bool noAccessCheck = (args[5] != 0); Object* valueObj = (Object*) args[6]; #ifdef WITH_TAINT_TRACKING /* rtaint = args[7] * thisPtr taint = args[8] * obj taint = args[9] * declaringClass taint = args[10] * fieldType taint = args[11] * slot taint = args[12] * noAccessCheck taint = args[13] */ u4 valueTaint = args[14]; #endif JValue* fieldPtr; JValue value; /* unwrap primitive, or verify object type */ if (!dvmUnwrapPrimitive(valueObj, fieldType, &value)) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "invalid value for field"); RETURN_VOID(); } /* get a pointer to the field's data; performs access checks */ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck); if (fieldPtr == NULL) RETURN_VOID(); /* store 4 or 8 bytes */ if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { fieldPtr->j = value.j; } else if (fieldType->primitiveType == PRIM_NOT) { if (slot < 0) { StaticField *sfield; sfield = (StaticField *)dvmSlotToField(declaringClass, slot); assert(fieldPtr == &sfield->value); dvmSetStaticFieldObject(sfield, value.l); } else { int offset = declaringClass->ifields[slot].byteOffset; assert(fieldPtr == (JValue *)BYTE_OFFSET(obj, offset)); dvmSetFieldObject(obj, offset, value.l); } } else { fieldPtr->i = value.i; } #ifdef WITH_TAINT_TRACKING /* If we got this far, we know the fields is okay to access and there * will not be a problem getting the field from the slot */ { Field* field = dvmSlotToField(declaringClass, slot); assert(field != NULL); if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*)field; dvmSetStaticFieldTaint(sfield, valueTaint); } else { /* Note, getFieldDataAddr() already checked that * obj is of type declaringClass, so no need to check here */ InstField* ifield = (InstField*)field; if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { dvmSetFieldTaintWide(obj, ifield->byteOffset, valueTaint); } else { dvmSetFieldTaint(obj, ifield->byteOffset, valueTaint); } } } #endif RETURN_VOID(); }
/* * private Object getField(Object o, Class declaringClass, Class type, * int slot, boolean noAccessCheck) * * Primitive types need to be boxed. */ static void Dalvik_java_lang_reflect_Field_getField(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* obj = (Object*) args[1]; ClassObject* declaringClass = (ClassObject*) args[2]; ClassObject* fieldType = (ClassObject*) args[3]; int slot = args[4]; bool noAccessCheck = (args[5] != 0); #ifdef WITH_TAINT_TRACKING u4* rtaint = (u4*) &args[6]; /* return taint tag slot */ #endif JValue value; const JValue* fieldPtr; DataObject* result; //dvmDumpClass(obj->clazz, kDumpClassFullDetail); /* get a pointer to the field's data; performs access checks */ fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck); if (fieldPtr == NULL) RETURN_VOID(); /* copy 4 or 8 bytes out */ if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { value.j = fieldPtr->j; } else { value.i = fieldPtr->i; } result = dvmWrapPrimitive(value, fieldType); dvmReleaseTrackedAlloc((Object*) result, NULL); #ifdef WITH_TAINT_TRACKING /* If we got this far, we know the fields is okay to access and there * will not be a problem getting the field from the slot */ { Field* field = dvmSlotToField(declaringClass, slot); assert(field != NULL); if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*)field; *rtaint = dvmGetStaticFieldTaint(sfield); } else { /* Note, getFieldDataAddr() already checked that * obj is of type declaringClass, so no need to check here */ InstField* ifield = (InstField*)field; if (fieldType->primitiveType == PRIM_LONG || fieldType->primitiveType == PRIM_DOUBLE) { *rtaint = dvmGetFieldTaintWide(obj, ifield->byteOffset); } else { *rtaint = dvmGetFieldTaint(obj, ifield->byteOffset); } } } #endif RETURN_PTR(result); }