mirror of
https://github.com/revanced/smali.git
synced 2025-05-29 20:20:12 +02:00
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:
parent
fffb29fd9d
commit
9e5dd85d83
@ -238,10 +238,31 @@ public class ClassPath {
|
|||||||
arrayDimensions = i;
|
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;
|
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() {
|
public int getArrayDimensions() {
|
||||||
return arrayDimensions;
|
return arrayDimensions;
|
||||||
}
|
}
|
||||||
|
@ -458,6 +458,8 @@ public class MethodAnalyzer {
|
|||||||
return handleNewInstance(analyzedInstruction);
|
return handleNewInstance(analyzedInstruction);
|
||||||
case NEW_ARRAY:
|
case NEW_ARRAY:
|
||||||
return handleNewArray(analyzedInstruction);
|
return handleNewArray(analyzedInstruction);
|
||||||
|
case FILLED_NEW_ARRAY:
|
||||||
|
return handleFilledNewArray(analyzedInstruction);
|
||||||
}
|
}
|
||||||
assert false;
|
assert false;
|
||||||
return false;
|
return false;
|
||||||
@ -946,6 +948,59 @@ public class MethodAnalyzer {
|
|||||||
return true;
|
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) {
|
private static void checkRegister(RegisterType registerType, EnumSet validCategories) {
|
||||||
if (!validCategories.contains(registerType.category)) {
|
if (!validCategories.contains(registerType.category)) {
|
||||||
//TODO: add expected categories to error message
|
//TODO: add expected categories to error message
|
||||||
|
@ -48,6 +48,7 @@ public class RegisterType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static enum Category {
|
public static enum Category {
|
||||||
|
//the Unknown category denotes a register type that hasn't been determined yet
|
||||||
Unknown,
|
Unknown,
|
||||||
Null,
|
Null,
|
||||||
One,
|
One,
|
||||||
@ -63,10 +64,19 @@ public class RegisterType {
|
|||||||
LongHi,
|
LongHi,
|
||||||
DoubleLo,
|
DoubleLo,
|
||||||
DoubleHi,
|
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,
|
UninitRef,
|
||||||
Reference,
|
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;
|
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 =
|
protected static Category[][] mergeTable =
|
||||||
{
|
{
|
||||||
/* Unknown Null One, Boolean Byte PosByte Short PosShort Char Integer, Float, LongLo LongHi DoubleLo DoubleHi UninitRef Reference Conflicted*/
|
/* 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},
|
/*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}
|
/*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) {
|
public static RegisterType getRegisterTypeForType(String type) {
|
||||||
switch (typeIdItem.getTypeDescriptor().charAt(0)) {
|
switch (type.charAt(0)) {
|
||||||
case 'V':
|
case 'V':
|
||||||
throw new ValidationException("The V type can only be used as a method return type");
|
throw new ValidationException("The V type can only be used as a method return type");
|
||||||
case 'Z':
|
case 'Z':
|
||||||
@ -113,12 +154,16 @@ public class RegisterType {
|
|||||||
return getRegisterType(Category.DoubleLo, null);
|
return getRegisterType(Category.DoubleLo, null);
|
||||||
case 'L':
|
case 'L':
|
||||||
case '[':
|
case '[':
|
||||||
return getRegisterType(Category.Reference, ClassPath.getClassDef(typeIdItem));
|
return getRegisterType(Category.Reference, ClassPath.getClassDef(type));
|
||||||
default:
|
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) {
|
public static RegisterType getWideRegisterTypeForTypeIdItem(TypeIdItem typeIdItem, boolean firstRegister) {
|
||||||
if (typeIdItem.getRegisterCount() == 1) {
|
if (typeIdItem.getRegisterCount() == 1) {
|
||||||
throw new RuntimeException("Cannot use this method for non-wide register type: " +
|
throw new RuntimeException("Cannot use this method for non-wide register type: " +
|
||||||
@ -192,6 +237,20 @@ public class RegisterType {
|
|||||||
return RegisterType.getRegisterType(mergedCategory, mergedType);
|
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) {
|
public static RegisterType getRegisterType(Category category, ClassDef classType) {
|
||||||
RegisterType newRegisterType = new RegisterType(category, classType);
|
RegisterType newRegisterType = new RegisterType(category, classType);
|
||||||
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
|
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user