Handle uninitialized referenced appropriately

git-svn-id: https://smali.googlecode.com/svn/trunk@610 55b6fa8a-2a1e-11de-a435-ffa8d773f76a
This commit is contained in:
JesusFreke@JesusFreke.com 2010-02-08 02:27:30 +00:00
parent b615ba6f51
commit a0314c265e
3 changed files with 75 additions and 2 deletions

View File

@ -1,6 +1,9 @@
package org.jf.dexlib.Code.Analysis;
import org.jf.dexlib.Code.*;
import org.jf.dexlib.Item;
import org.jf.dexlib.ItemType;
import org.jf.dexlib.MethodIdItem;
import org.jf.dexlib.Util.ExceptionWithContext;
import java.util.LinkedList;
@ -100,6 +103,23 @@ public class AnalyzedInstruction {
return mergedRegisterType;
}
protected boolean isInvokeInit() {
if (instruction.opcode != Opcode.INVOKE_DIRECT && instruction.opcode != Opcode.INVOKE_DIRECT_RANGE) {
return false;
}
InstructionWithReference instruction = (InstructionWithReference)this.instruction;
Item item = instruction.getReferencedItem();
assert item.getItemType() == ItemType.TYPE_METHOD_ID_ITEM;
MethodIdItem method = (MethodIdItem)item;
if (!method.getMethodName().equals("<init>")) {
return false;
}
return true;
}
public boolean setsRegister() {
return instruction.opcode.setsRegister();
}
@ -109,10 +129,33 @@ public class AnalyzedInstruction {
}
public boolean setsRegister(int registerNumber) {
//When constructing a new object, the register type will be an uninitialized reference after the new-instance
//instruction, but becomes an initialized reference once the <init> method is called. So even though invoke
//instructions don't normally change any registers, calling an <init> method will change the type of its
//object register. If the uninitialized reference has been copied to other registers, they will be initialized
//as well, so we need to check for that too
if (isInvokeInit()) {
int destinationRegister = ((FiveRegisterInstruction)instruction).getRegisterD();
if (registerNumber == destinationRegister) {
return true;
}
RegisterType preInstructionDestRegisterType = getMergedRegisterTypeFromPredecessors(destinationRegister);
if (preInstructionDestRegisterType.category != RegisterType.Category.UninitRef) {
return false;
}
//check if the uninit ref has been copied to another register
if (getMergedRegisterTypeFromPredecessors(registerNumber) == preInstructionDestRegisterType) {
return true;
}
return false;
}
if (!setsRegister()) {
return false;
}
int destinationRegister = getDestinationRegister();
if (registerNumber == destinationRegister) {
return true;
}

View File

@ -1024,6 +1024,28 @@ public class MethodAnalyzer {
private boolean handleNewInstance(AnalyzedInstruction analyzedInstruction) {
InstructionWithReference instruction = (InstructionWithReference)analyzedInstruction.instruction;
int register = ((SingleRegisterInstruction)analyzedInstruction.instruction).getRegisterA();
RegisterType destRegisterType = analyzedInstruction.postRegisterMap[register];
if (destRegisterType.category != RegisterType.Category.Unknown) {
assert destRegisterType.category == RegisterType.Category.UninitRef;
//the "post-instruction" destination register will only be set if we've gone over
//this instruction at least once before. If this is the case, then we need to check
//all the other registers, and make sure that none of them contain the same
//uninitialized reference that is in the destination register.
for (int i=0; i<analyzedInstruction.postRegisterMap.length; i++) {
if (i==register) {
continue;
}
if (analyzedInstruction.getPreInstructionRegisterType(i) == destRegisterType) {
throw new ValidationException(String.format("Register v%d contains an uninitialized reference " +
"that was created by this new-instance instruction.", i));
}
}
}
Item item = instruction.getReferencedItem();
assert item.getItemType() == ItemType.TYPE_TYPE_ID_ITEM;
@ -1036,7 +1058,7 @@ public class MethodAnalyzer {
}
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction,
RegisterType.getRegisterType(RegisterType.Category.UninitRef, classType.type));
RegisterType.getUnitializedReference(classType.type));
return true;
}

View File

@ -95,7 +95,7 @@ public class RegisterType {
/*LongHi*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, LongHi, Conflicted, Conflicted, Conflicted},
/*DoubleLo*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongLo, Conflicted, DoubleLo, Conflicted, Conflicted, Conflicted, Conflicted},
/*DoubleHi*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, LongHi, Conflicted, DoubleHi, Conflicted, Conflicted, Conflicted},
/*UninitRef*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, UninitRef, Conflicted, Conflicted},
/*UninitRef*/ {Unknown, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, Conflicted, 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}
};
@ -251,6 +251,14 @@ public class RegisterType {
return false;
}
public static RegisterType getUnitializedReference(ClassDef classType) {
//We always create a new RegisterType instance for an uninit ref. Each unique uninit RegisterType instance
//is used to track a specific uninitialized reference, so that if multiple registers contain the same
//uninitialized reference, then they can all be upgraded to an initialized reference when the appropriate
//<init> is invoked
return new RegisterType(Category.UninitRef, classType);
}
public static RegisterType getRegisterType(Category category, ClassDef classType) {
RegisterType newRegisterType = new RegisterType(category, classType);
RegisterType internedRegisterType = internedRegisterTypes.get(newRegisterType);