Skip to content

Commit

Permalink
Merge pull request #2115 from MarcMil/fixes
Browse files Browse the repository at this point in the history
Fix a bug with explicit cassts in dex and improve performance of type assigner
  • Loading branch information
StevenArzt authored Oct 22, 2024
2 parents 20c6ed8 + 987955d commit 0069dc3
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 9 deletions.
92 changes: 90 additions & 2 deletions src/main/java/soot/dexpler/DexBody.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
import soot.dexpler.tags.FloatOpTag;
import soot.dexpler.tags.IntOpTag;
import soot.dexpler.tags.IntOrFloatOpTag;
import soot.dexpler.tags.LongOpTag;
import soot.dexpler.tags.LongOrDoubleOpTag;
import soot.dexpler.tags.ShortOpTag;
import soot.dexpler.typing.DalvikTyper;
Expand All @@ -125,6 +126,7 @@
import soot.jimple.DoubleConstant;
import soot.jimple.EqExpr;
import soot.jimple.FloatConstant;
import soot.jimple.GotoStmt;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
Expand All @@ -133,6 +135,8 @@
import soot.jimple.LongConstant;
import soot.jimple.MulExpr;
import soot.jimple.NeExpr;
import soot.jimple.NegExpr;
import soot.jimple.NopStmt;
import soot.jimple.NullConstant;
import soot.jimple.NumericConstant;
import soot.jimple.OrExpr;
Expand Down Expand Up @@ -877,6 +881,15 @@ public Type promote(Type tlow, Type thigh) {

}

protected Type getDefiniteType(Local v) {
Collection<Type> r = definiteConstraints.get(v);
if (r != null && r.size() == 1) {
return r.iterator().next();
} else {
return null;
}
}

protected soot.jimple.toolkits.typing.fast.BytecodeHierarchy createBytecodeHierarchy() {
return new soot.jimple.toolkits.typing.fast.BytecodeHierarchy() {
public java.util.Collection<Type> lcas(Type a, Type b, boolean useWeakObjectType) {
Expand Down Expand Up @@ -905,6 +918,18 @@ protected Collection<Type> reduceToAllowedTypesForLocal(Collection<Type> lcas, L
if (constraints.isEmpty()) {
return lcas;
}
if (lcas.size() == 1) {
Type e = lcas.iterator().next();
//Only one element, we can check this directly
if (!constraints.contains(e)) {
// No typing left
Set<Type> res = new HashSet<>(constraints);
res.add(e);
return res;
} else {
return lcas;
}
}
Set<Type> res = new HashSet<>(lcas);
res.retainAll(constraints);
if (res.isEmpty()) {
Expand All @@ -926,6 +951,15 @@ protected CastInsertionUseVisitor createCastInsertionUseVisitor(soot.jimple.tool
soot.jimple.toolkits.typing.fast.IHierarchy h, boolean countOnly) {
return new CastInsertionUseVisitor(countOnly, jBody, tg, h) {

@Override
protected boolean eliminateUnnecessaryCasts() {
//We do not want to eliminate casts that were explicitly present in the original dex code
//Otherwise we have problems in certain edge cases, were our typings are suboptimal
//with respect to float/int and double/long
return false;
}

@Override
protected NeedCastResult needCast(Type target, Type from, IHierarchy h) {
NeedCastResult r = super.needCast(target, from, h);
if (r == NeedCastResult.NEEDS_CAST) {
Expand Down Expand Up @@ -984,6 +1018,23 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) {

}
}.inferTypes();
for (Unit u : jBody.getUnits()) {
Stmt s = (Stmt) u;
if (s.containsArrayRef() && s instanceof AssignStmt) {
AssignStmt assign = (AssignStmt) s;
Value lop = assign.getLeftOp();
Value rop = assign.getRightOp();
if (lop.getType() instanceof FloatType && rop instanceof IntConstant) {
IntConstant intC = (IntConstant) rop;
assign.setRightOp(FloatConstant.v(Float.intBitsToFloat(intC.value)));
}
if (lop.getType() instanceof DoubleType && rop instanceof LongConstant) {
LongConstant longC = (LongConstant) rop;
assign.setRightOp(DoubleConstant.v(Double.longBitsToDouble(longC.value)));
}
}
}

checkUnrealizableCasts();

// Shortcut: Reduce array initializations
Expand Down Expand Up @@ -1214,6 +1265,20 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) {
// Check that we don't have anything weird
checkUnrealizableCasts();

UnitPatchingChain units = jBody.getUnits();
Unit u = units.getFirst();
while (u != null) {
if (u instanceof GotoStmt) {
GotoStmt gt = (GotoStmt) u;
if (gt.getTarget() == gt) {
//There are crazy cases like that in the wild.
NopStmt nop = jimple.newNopStmt();
units.insertBefore(nop, u);
gt.setTarget(nop);
}
}
u = units.getSuccOf(u);
}
// t_whole_jimplification.end();

return jBody;
Expand Down Expand Up @@ -1248,6 +1313,7 @@ private void handleKnownDexArrayTypes(Body b, Jimple jimple, MultiMap<Local, Typ
while (u != null) {
if (u instanceof AssignStmt) {
AssignStmt assign = ((AssignStmt) u);
Value lop = assign.getLeftOp();
Value rop = assign.getRightOp();
if (rop instanceof ArrayRef) {
for (Tag tg : u.getTags()) {
Expand Down Expand Up @@ -1312,6 +1378,28 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) {
if (u instanceof AssignStmt) {
AssignStmt def = (AssignStmt) u;
Value rop = def.getRightOp();
if (rop instanceof NegExpr) {
boolean isDouble = u.hasTag(DoubleOpTag.NAME);
boolean isFloat = u.hasTag(FloatOpTag.NAME);
boolean isLong = u.hasTag(LongOpTag.NAME);
NegExpr neg = ((NegExpr) rop);
Value op = neg.getOp();
Type t = null;
//As for ints, shorts etc.: the type assigner
//already handles this automatically
if (isDouble) {
t = DoubleType.v();
} else if (isFloat) {
t = FloatType.v();
} else if (isLong) {
t = LongType.v();
}
if (t != null) {
Local l = (Local) op;
l.setType(t);
}

}
if (rop instanceof BinopExpr) {
boolean isDouble = u.hasTag(DoubleOpTag.NAME);
boolean isFloat = u.hasTag(FloatOpTag.NAME);
Expand Down Expand Up @@ -1425,10 +1513,10 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) {
}
if (rop instanceof Constant) {
Constant c = (Constant) assign.getRightOp();
if (tl instanceof DoubleType) {
if (tl instanceof DoubleType && c instanceof LongConstant) {
long vVal = ((LongConstant) c).value;
assign.setRightOp(DoubleConstant.v(Double.longBitsToDouble(vVal)));
} else if (tl instanceof FloatType) {
} else if (tl instanceof FloatType && c instanceof IntConstant) {
int vVal = ((IntConstant) c).value;
assign.setRightOp(FloatConstant.v(Float.intBitsToFloat(vVal)));
}
Expand Down
29 changes: 27 additions & 2 deletions src/main/java/soot/jimple/toolkits/scalar/Evaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import soot.jimple.CmplExpr;
import soot.jimple.Constant;
import soot.jimple.DivExpr;
import soot.jimple.DoubleConstant;
import soot.jimple.EqExpr;
import soot.jimple.FloatConstant;
import soot.jimple.GeExpr;
import soot.jimple.GtExpr;
import soot.jimple.IntConstant;
Expand Down Expand Up @@ -119,8 +121,8 @@ public static Value getConstantValueOf(Value op) {
}
} else if (op instanceof BinopExpr) {
final BinopExpr binExpr = (BinopExpr) op;
final Value c1 = getConstantValueOf(binExpr.getOp1());
final Value c2 = getConstantValueOf(binExpr.getOp2());
Value c1 = getConstantValueOf(binExpr.getOp1());
Value c2 = getConstantValueOf(binExpr.getOp2());

if (op instanceof AddExpr) {
return ((NumericConstant) c1).add((NumericConstant) c2);
Expand Down Expand Up @@ -174,6 +176,10 @@ public static Value getConstantValueOf(Value op) {
throw new IllegalArgumentException("CmpExpr: LongConstant(s) expected");
}
} else if ((op instanceof CmpgExpr) || (op instanceof CmplExpr)) {
//In Dalvik code:
//int <-> float and long <-> double are equivalent essentially.
c1 = convertToFloatOrDouble(c1);
c2 = convertToFloatOrDouble(c2);
if ((c1 instanceof RealConstant) && (c2 instanceof RealConstant)) {
if (op instanceof CmpgExpr) {
return ((RealConstant) c1).cmpg((RealConstant) c2);
Expand All @@ -191,4 +197,23 @@ public static Value getConstantValueOf(Value op) {
throw new RuntimeException("couldn't getConstantValueOf of: " + op);
} // getConstantValueOf

/**
* For Android Dex:
*
* Converts int and long constants to their corresponding float and double counterparts
* @param c the constant
* @return the potentially changed value
*/
private static Value convertToFloatOrDouble(Value c) {
if (c instanceof IntConstant) {
IntConstant ic = (IntConstant) c;
return FloatConstant.v(Float.intBitsToFloat(ic.value));
} else if (c instanceof LongConstant) {
LongConstant ic = (LongConstant) c;
return DoubleConstant.v(Double.longBitsToDouble(ic.value));
}

return c;
}

} // Evaluator
24 changes: 21 additions & 3 deletions src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ public class CastInsertionUseVisitor implements IUseVisitor {

private final boolean countOnly;
private int count;
protected boolean eliminateUnnecessaryCasts = eliminateUnnecessaryCasts();

public CastInsertionUseVisitor(boolean countOnly, JimpleBody jb, Typing tg, IHierarchy h) {
this.jb = jb;
Expand All @@ -246,6 +247,10 @@ public CastInsertionUseVisitor(boolean countOnly, JimpleBody jb, Typing tg, IHie
this.count = 0;
}

protected boolean eliminateUnnecessaryCasts() {
return true;
}

@Override
public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) {
Type t = AugEvalFunction.eval_(this.tg, op, stmt, this.jb);
Expand All @@ -254,7 +259,7 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) {
CastExpr ce = (CastExpr) op;
// by default, t only checks for the type of the cast target
t = AugEvalFunction.eval_(this.tg, ce.getOp(), stmt, this.jb);
if (ce.getType() == t) {
if (eliminateUnnecessaryCasts && ce.getType() == t) {
// no cast necessary!
return ce.getOp();
}
Expand Down Expand Up @@ -560,6 +565,14 @@ protected Collection<Typing> applyAssignmentConstraints(Typing tg, IEvalFunction
Value lhs = stmt.getLeftOp();
if (lhs instanceof Local) {
Local v = (Local) lhs;
Type t = getDefiniteType(v);
if (t != null) {
simple.set(i);
wl.clear(i);
tg.set(v, t);
continue;
}

if (singleAssignments.contains(v)) {
Collection<Type> d = ef.eval(tg, stmt.getRightOp(), stmt);
if (d.size() == 1) {
Expand All @@ -586,6 +599,7 @@ protected Collection<Typing> applyAssignmentConstraints(Typing tg, IEvalFunction
}

Set<Type> throwable = null;
BottomType bt = BottomType.v();

while (!sigma.isEmpty()) {
WorklistElement element = sigma.element();
Expand Down Expand Up @@ -632,7 +646,7 @@ protected Collection<Typing> applyAssignmentConstraints(Typing tg, IEvalFunction
lcas = throwable;
} else {
Type featureType = ds.getTypeDecision(told, t_);
if (!typesEqual(featureType, BottomType.v())) {
if (!typesEqual(featureType, bt)) {
// Use feature type.
lcas = Collections.singleton(featureType);
} else {
Expand Down Expand Up @@ -666,7 +680,7 @@ protected Collection<Typing> applyAssignmentConstraints(Typing tg, IEvalFunction
sigma.add(e);
}

if (!typesEqual(told, BottomType.v()) && !typesEqual(t_, BottomType.v())) {
if (!typesEqual(told, bt) && !typesEqual(t_, bt)) {
// 't' is base class of type 'told' & 't_';
// It will decide the feature type by target value.
TypeContainer container = new TypeContainer(told, t_, t);
Expand All @@ -693,6 +707,10 @@ protected Collection<Typing> applyAssignmentConstraints(Typing tg, IEvalFunction
return r;
}

protected Type getDefiniteType(Local v) {
return null;
}

protected Collection<Type> reduceToAllowedTypesForLocal(Collection<Type> lcas, Local v) {
return lcas;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,12 @@ public void caseAssignStmt(AssignStmt stmt) {
// At the very least, the the type for this array should be whatever its
// base type is
et = bt;
logger.warn("Could not find any indication on the array type of " + stmt + " in " + jb.getMethod().getSignature(),
", assuming its base type is " + bt);
if (logger.isDebugEnabled()) {
//This can happen in rare cases in Android for int/float and long/double arrays
logger.debug(
"Could not find any indication on the array type of " + stmt + " in " + jb.getMethod().getSignature(),
", assuming its base type is " + bt);
}
}

at = et.makeArrayType();
Expand Down

0 comments on commit 0069dc3

Please sign in to comment.