From b1b2022506a7f73c7f64e536f1c2525bb497a26f Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 17:21:57 +0200 Subject: [PATCH 1/6] Fix a bug with explicit cassts in dex and improve performance of typer slightly --- src/main/java/soot/dexpler/DexBody.java | 21 +++++++++++++++++++ .../toolkits/typing/fast/TypeResolver.java | 12 ++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index 43ab33e55c6..ba144e6ee4d 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -905,6 +905,18 @@ protected Collection reduceToAllowedTypesForLocal(Collection 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 res = new HashSet<>(constraints); + res.add(e); + return res; + } else { + return lcas; + } + } Set res = new HashSet<>(lcas); res.retainAll(constraints); if (res.isEmpty()) { @@ -926,6 +938,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) { diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 589268f3f1c..5cda8e8411f 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -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; @@ -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); @@ -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(); } @@ -586,6 +591,7 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction } Set throwable = null; + BottomType bt = BottomType.v(); while (!sigma.isEmpty()) { WorklistElement element = sigma.element(); @@ -632,7 +638,7 @@ protected Collection 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 { @@ -666,7 +672,7 @@ protected Collection 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); From 7bf5025b9232849d616809ce90aafdd389c27750 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 18:29:58 +0200 Subject: [PATCH 2/6] Log lack of array type only in debug mode --- .../java/soot/jimple/toolkits/typing/fast/UseChecker.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java index abf16ee086b..bfe99f653ea 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java @@ -395,8 +395,11 @@ 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(); From 2257f5316e9058d72fdd096fe04e67e2e5612b55 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 18:44:09 +0200 Subject: [PATCH 3/6] checkstyle --- src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java index bfe99f653ea..2ad671b5eb6 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/UseChecker.java @@ -395,11 +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; - if (logger.isDebugEnabled()) + 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(); From 989fc81f24e74614214ac22736d6023f909472a1 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 20:00:55 +0200 Subject: [PATCH 4/6] Fix constant evaluator bug for Dalvik --- .../jimple/toolkits/scalar/Evaluator.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/soot/jimple/toolkits/scalar/Evaluator.java b/src/main/java/soot/jimple/toolkits/scalar/Evaluator.java index 453d82d16a5..4ad35189707 100644 --- a/src/main/java/soot/jimple/toolkits/scalar/Evaluator.java +++ b/src/main/java/soot/jimple/toolkits/scalar/Evaluator.java @@ -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; @@ -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); @@ -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); @@ -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 From 329968017afe042a5defcf39453d611aa7068fdb Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 23:59:43 +0200 Subject: [PATCH 5/6] Improve typer --- src/main/java/soot/dexpler/DexBody.java | 62 ++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index ba144e6ee4d..a3c259d6e34 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -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; @@ -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; @@ -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; @@ -1005,6 +1009,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 @@ -1235,6 +1256,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; @@ -1269,6 +1304,7 @@ private void handleKnownDexArrayTypes(Body b, Jimple jimple, MultiMap Date: Tue, 22 Oct 2024 00:33:44 +0200 Subject: [PATCH 6/6] Performance --- src/main/java/soot/dexpler/DexBody.java | 9 +++++++++ .../jimple/toolkits/typing/fast/TypeResolver.java | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index a3c259d6e34..65b82362603 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -881,6 +881,15 @@ public Type promote(Type tlow, Type thigh) { } + protected Type getDefiniteType(Local v) { + Collection 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 lcas(Type a, Type b, boolean useWeakObjectType) { diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 5cda8e8411f..24fe45fd4a5 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -565,6 +565,14 @@ protected Collection 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 d = ef.eval(tg, stmt.getRightOp(), stmt); if (d.size() == 1) { @@ -699,6 +707,10 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction return r; } + protected Type getDefiniteType(Local v) { + return null; + } + protected Collection reduceToAllowedTypesForLocal(Collection lcas, Local v) { return lcas; }