Implemented verification for filled-new-array

git-svn-id: https://smali.googlecode.com/svn/trunk@575 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2010-01-24 03:06:38 +00:00
parent fffb29fd9d
commit 9e5dd85d83
3 changed files with 140 additions and 5 deletions

View File

@ -238,10 +238,31 @@ public class ClassPath {
arrayDimensions = i;
}
public ClassDef getElementClass() {
/**
* Returns the "base" element class of the array.
*
* For example, for a multi-dimensional array of strings ([[Ljava/lang/String;), this method would return
* Ljava/lang/String;
* @return
*/
public ClassDef getBaseElementClass() {
return elementClass;
}
/**
* Returns the "immediate" element class of the array.
*
* For example, for a multi-dimensional array of stings with 2 dimensions ([[Ljava/lang/String;), this method
* would return [Ljava/lang/String;
* @return
*/
public ClassDef getImmediateElementClass() {
if (arrayDimensions == 1) {
return elementClass;
}
return getArrayClassDefByElementClassAndDimension(elementClass, arrayDimensions - 1);
}
public int getArrayDimensions() {
return arrayDimensions;
}

View File

@ -458,6 +458,8 @@ public class MethodAnalyzer {
return handleNewInstance(analyzedInstruction);
case NEW_ARRAY:
return handleNewArray(analyzedInstruction);
case FILLED_NEW_ARRAY:
return handleFilledNewArray(analyzedInstruction);
}
assert false;
return false;
@ -946,6 +948,59 @@ public class MethodAnalyzer {
return true;
}
private boolean handleFilledNewArray(AnalyzedInstruction analyzedInstruction) {
RegisterType arrayType;
RegisterType arrayImmediateElementType;
{
InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
Item item = instruction.getReferencedItem();
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
ClassPath.ClassDef classDef = ClassPath.getClassDef((TypeIdItem)item);
if (classDef.getClassType().charAt(0) != '[') {
throw new ValidationException("Cannot use non-array type \"" + classDef.getClassType() +
"\" with new-array. Use new-instance instead.");
}
ClassPath.ArrayClassDef arrayClassDef = (ClassPath.ArrayClassDef)classDef;
arrayType = RegisterType.getRegisterType(RegisterType.Category.Reference, classDef);
arrayImmediateElementType = RegisterType.getRegisterTypeForType(
arrayClassDef.getImmediateElementClass().getClassType());
String baseElementType = arrayClassDef.getBaseElementClass().getClassType();
if (baseElementType.charAt(0) == 'J' || baseElementType.charAt(0) == 'D') {
throw new ValidationException("Cannot use filled-new-array to create an array of wide values " +
"(long or double)");
}
}
FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
int registerCount = instruction.getRegCount();
byte[] registers = new byte[]{instruction.getRegisterD(), instruction.getRegisterE(), instruction.getRegisterF(),
instruction.getRegisterG(), instruction.getRegisterA()};
for (int i=0; i<registerCount; i++) {
int register = registers[i];
RegisterType elementType = analyzedInstruction.getPreInstructionRegisterType(register);
assert elementType != null;
if (elementType.category == RegisterType.Category.Unknown) {
return false;
}
if (!elementType.canBeAssignedTo(arrayImmediateElementType)) {
throw new ValidationException("Register v" + Integer.toString(register) + " is of type " +
elementType.toString() + " and is incompatible with the array type " +
arrayType.type.getClassType());
}
}
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, arrayType);
return true;
}
private static void checkRegister(RegisterType registerType, EnumSet validCategories) {
if (!validCategories.contains(registerType.category)) {
//TODO: add expected categories to error message

View File

@ -48,6 +48,7 @@ public class RegisterType {
}
public static enum Category {
//the Unknown category denotes a register type that hasn't been determined yet
Unknown,
Null,
One,
@ -63,10 +64,19 @@ public class RegisterType {
LongHi,
DoubleLo,
DoubleHi,
//the UninitRef category is used after a new-instance operation, and before the corresponding <init> is called
//it is also used for the "this" register inside an <init> method, before the superclass' <init> method is
//called
UninitRef,
Reference,
//This is used when there are multiple incoming execution paths that have incompatible register types. For
//example if the register's type is an Integer on one incomming code path, but is a Reference type on another
//incomming code path. There is no register type that can hold either an Integer or a Reference.
Conflicted;
//this table is used when merging register types. For example, if a particular register can be either a Byte
//or a Char, then the "merged" type of that register would be Integer, because it is the "smallest" type can
//could hold either type of value.
protected static Category[][] mergeTable =
{
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted*/
@ -89,10 +99,41 @@ public class RegisterType {
/*Reference*/ {Unknown, Reference, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Reference, Conflicted},
/*Conflicted*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted}
};
//this table is used to denote whether a given value type can be assigned to a "slot" of a certain type. For
//example, to determine if you can assign a Boolean value to a particular array "slot", where the array is an
//array of Integers, you would look up assignmentTable[Boolean.ordinal()][Integer.ordinal()]
//Note that not all slot types in the table are expected to be used. For example, it doesn't make sense to
//check if a value can be assigned to an uninitialized reference slot - because there is no such thing.
protected static boolean[][] assigmentTable =
{
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted |slot type*/
/*Unknown*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Null*/ {false, true, false, true, true, true, true, true, true, true, true, false, false, false, false, false, true, false},
/*One*/ {false, false, true, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
/*Boolean*/ {false, false, false, true, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
/*Byte*/ {false, false, false, false, true, false, true, true, false, true, true, false, false, false, false, false, false, false},
/*PosByte*/ {false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false},
/*Short*/ {false, false, false, false, false, false, true, false, false, true, true, false, false, false, false, false, false, false},
/*PosShort*/ {false, false, false, false, false, false, true, true, true, true, true, false, false, false, false, false, false, false},
/*Char*/ {false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false},
/*Integer*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false},
/*Float*/ {false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false},
/*LongLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*LongHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
/*DoubleLo*/ {false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false, false},
/*DoubleHi*/ {false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, false, false, false},
/*UninitRef*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
/*Reference*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
/*Conflicted*/ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}
/*----------*/
/*value type*/
};
}
public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
switch (typeIdItem.getTypeDescriptor().charAt(0)) {
public static RegisterType getRegisterTypeForType(String type) {
switch (type.charAt(0)) {
case 'V':
throw new ValidationException("The V type can only be used as a method return type");
case 'Z':
@ -113,12 +154,16 @@ public class RegisterType {
return getRegisterType(Category.DoubleLo, null);
case 'L':
case '[':
return getRegisterType(Category.Reference, ClassPath.getClassDef(typeIdItem));
return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
default:
throw new RuntimeException("Invalid type: " + typeIdItem.getTypeDescriptor());
throw new RuntimeException("Invalid type: " + type);
}
}
public static RegisterType getRegisterTypeForTypeIdItem(TypeIdItem typeIdItem) {
return getRegisterTypeForType(typeIdItem.getTypeDescriptor());
}
public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
if (typeIdItem.getRegisterCount() == 1) {
throw new RuntimeException("Cannot use this method for non-wide register type: " +
@ -192,6 +237,20 @@ public class RegisterType {
return RegisterType.getRegisterType(mergedCategory, mergedType);
}
public boolean canBeAssignedTo(RegisterType slotType) {
if (Category.assigmentTable[this.category.ordinal()][slotType.category.ordinal()]) {
if (this.category == Category.Reference && slotType.category == Category.Reference) {
if (!slotType.type.isInterface()) {
return this.type.extendsClass(slotType.type);
}
//for verification, we assume all objects implement all interfaces, so we don't verify the type if
//slotType is an interface
}
return true;
}
return false;
}
public static RegisterType getRegisterType(Category category, ClassDef classType) {
RegisterType newRegisterType = new RegisterType(category, classType);
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);