From 9e5dd85d837501e84e18617fc136c8203ab1f183 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Sun, 24 Jan 2010 03:06:38 +0000 Subject: [PATCH] Implemented verification for filled-new-array git-svn-id: https://smali.googlecode.com/svn/trunk@575 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../jf/dexlib/Code/Analysis/ClassPath.java | 23 ++++++- .../dexlib/Code/Analysis/MethodAnalyzer.java | 55 +++++++++++++++ .../jf/dexlib/Code/Analysis/RegisterType.java | 67 +++++++++++++++++-- 3 files changed, 140 insertions(+), 5 deletions(-) diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java index 8b40df6d..c64e37e9 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/ClassPath.java @@ -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; } diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java index 893f805e..d4f32c78 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -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 is called + //it is also used for the "this" register inside an method, before the superclass' 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);