From 16dd9062e8de5010a69e5ba028b15a85e24b1646 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Tue, 14 Jan 2025 16:02:18 +0100 Subject: [PATCH 1/2] Do not alter semantics when propagating class constants Class constants can trigger NoClassDefFoundErrors, so we must be careful to not propagate them into other sections where the trap handler is different. --- .../soot/dexpler/DalvikThrowAnalysis.java | 4 + .../scalar/ConstantPropagatorUtils.java | 108 ++++++++++++++++++ .../toolkits/scalar/CopyPropagator.java | 7 +- .../exceptions/UnitThrowAnalysis.java | 1 + .../FlowSensitiveConstantPropagator.java | 10 +- 5 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 src/main/java/soot/jimple/toolkits/scalar/ConstantPropagatorUtils.java diff --git a/src/main/java/soot/dexpler/DalvikThrowAnalysis.java b/src/main/java/soot/dexpler/DalvikThrowAnalysis.java index 3632b8a6e63..67100eb0eb1 100644 --- a/src/main/java/soot/dexpler/DalvikThrowAnalysis.java +++ b/src/main/java/soot/dexpler/DalvikThrowAnalysis.java @@ -48,6 +48,7 @@ import soot.NullType; import soot.PrimType; import soot.RefLikeType; +import soot.RefType; import soot.Scene; import soot.Singletons; import soot.SootMethod; @@ -297,6 +298,9 @@ public void caseClassConstant(ClassConstant c) { // would be detected there. // // result = result.add(mgr.RESOLVE_CLASS_ERRORS); + + // that being said, a NoClassDefFoundError is possible nonetheless. + result = result.add(RefType.v("java.lang.NoClassDefFoundError")); } @Override diff --git a/src/main/java/soot/jimple/toolkits/scalar/ConstantPropagatorUtils.java b/src/main/java/soot/jimple/toolkits/scalar/ConstantPropagatorUtils.java new file mode 100644 index 00000000000..9e55bd3ee91 --- /dev/null +++ b/src/main/java/soot/jimple/toolkits/scalar/ConstantPropagatorUtils.java @@ -0,0 +1,108 @@ +package soot.jimple.toolkits.scalar; + +import soot.Body; +import soot.FastHierarchy; +import soot.RefType; +import soot.Scene; +import soot.Trap; +import soot.Unit; +import soot.UnitPatchingChain; +import soot.Value; +import soot.ValueBox; +import soot.jimple.ClassConstant; +import soot.jimple.DefinitionStmt; +import soot.toolkits.graph.ExceptionalUnitGraph; +import soot.toolkits.graph.ExceptionalUnitGraph.ExceptionDest; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2000 Patrick Lam + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * Contains utility methods for constant propagation + */ +public class ConstantPropagatorUtils { + + /** + * Checks whether propagating propagatedValue from defFrom to defTo is safe. + * @param graph an exceptional unit graph + * @param propagatedValue the propagated value + * @param defFrom definition source + * @param defTo target + * @param targetBox the target box + * @return true if and only if propagation is safe w.r.t. trap handling + */ + public static boolean mayPropagate(ExceptionalUnitGraph graph, Value propagatedValue, DefinitionStmt defFrom, Unit defTo, + ValueBox targetBox) { + if (!targetBox.canContainValue(propagatedValue)) { + return false; + } + if (propagatedValue instanceof ClassConstant) { + //Class Constants can trigger a NoClassDefFoundError. + //Therefore, we must not propagate them, since we might change the semantics of the original + //program w.r.t. traps. + RefType rt = RefType.v("java.lang.NoClassDefFoundError"); + Trap trap = null; + for (ExceptionDest d : graph.getExceptionDests(defFrom)) { + if (d.getThrowables().catchableAs(rt)) { + trap = d.getTrap(); + break; + } + } + Body body = graph.getBody(); + FastHierarchy fh = Scene.v().getOrMakeFastHierarchy(); + UnitPatchingChain chain = body.getUnits(); + + //this is not super fast, but the exceptional unit graph is not helpful here, since + //we would need to rebuild it after propagation to reflect the changes, which would be more expensive. + for (Trap i : body.getTraps()) { + if (trap != null && i.getHandlerUnit() != trap.getHandlerUnit()) { + continue; + } + if (fh.canStoreType(rt, i.getException().getType())) { + Unit u = i.getBeginUnit(); + while (u != i.getEndUnit()) { + if (u == defTo) { + //when the original code had the same handling unit, this is fine. + //if there is none, this is not fine. + return trap != null; + } + u = chain.getSuccOf(u); + } + } + } + //we have not found a trap, so the original code must not have one, either + return trap == null; + } + + return true; + } + + private static Unit getTrapHandler(ExceptionalUnitGraph graph, Unit def, RefType rtException) { + for (ExceptionDest d : graph.getExceptionDests(def)) { + if (d.getThrowables().catchableAs(rtException)) { + return d.getHandlerNode(); + } + } + return null; + } + +} diff --git a/src/main/java/soot/jimple/toolkits/scalar/CopyPropagator.java b/src/main/java/soot/jimple/toolkits/scalar/CopyPropagator.java index b2b7b3ff40d..329635bd743 100644 --- a/src/main/java/soot/jimple/toolkits/scalar/CopyPropagator.java +++ b/src/main/java/soot/jimple/toolkits/scalar/CopyPropagator.java @@ -57,9 +57,9 @@ import soot.tagkit.SourceLnPosTag; import soot.tagkit.Tag; import soot.toolkits.exceptions.ThrowAnalysis; +import soot.toolkits.graph.ExceptionalUnitGraph; import soot.toolkits.graph.ExceptionalUnitGraphFactory; import soot.toolkits.graph.PseudoTopologicalOrderer; -import soot.toolkits.graph.UnitGraph; import soot.toolkits.scalar.LocalDefs; public class CopyPropagator extends BodyTransformer { @@ -137,7 +137,7 @@ protected void internalTransform(Body b, String phaseName, Map o int fastCopyPropagationCount = 0; int slowCopyPropagationCount = 0; - UnitGraph graph + ExceptionalUnitGraph graph = ExceptionalUnitGraphFactory.createExceptionalUnitGraph(b, throwAnalysis, forceOmitExceptingUnitEdges); LocalDefs localDefs = G.v().soot_toolkits_scalar_LocalDefsFactory().newLocalDefs(graph); CPOptions options = new CPOptions(opts); @@ -192,10 +192,11 @@ protected void internalTransform(Body b, String phaseName, Map o final Value rightOp = def.getRightOp(); if (rightOp instanceof Constant) { - if (useBox.canContainValue(rightOp)) { + if (ConstantPropagatorUtils.mayPropagate(graph, rightOp, def, u, useBox)) { useBox.setValue(rightOp); copyLineTags(useBox, def); } + } else if (rightOp instanceof CastExpr) { CastExpr ce = (CastExpr) rightOp; if (ce.getCastType() instanceof RefLikeType) { diff --git a/src/main/java/soot/toolkits/exceptions/UnitThrowAnalysis.java b/src/main/java/soot/toolkits/exceptions/UnitThrowAnalysis.java index cef1d8e485d..4927a391561 100644 --- a/src/main/java/soot/toolkits/exceptions/UnitThrowAnalysis.java +++ b/src/main/java/soot/toolkits/exceptions/UnitThrowAnalysis.java @@ -892,6 +892,7 @@ public void caseStringConstant(StringConstant c) { @Override public void caseClassConstant(ClassConstant c) { + result = result.add(RefType.v("java.lang.NoClassDefFoundError")); } @Override diff --git a/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java b/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java index 47f8f54cca2..6a1e8842c83 100644 --- a/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java +++ b/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java @@ -44,6 +44,7 @@ import soot.dexpler.DexNullArrayRefTransformer; import soot.dexpler.DexNullThrowTransformer; import soot.jimple.AssignStmt; +import soot.jimple.ClassConstant; import soot.jimple.CmpgExpr; import soot.jimple.CmplExpr; import soot.jimple.Constant; @@ -344,7 +345,14 @@ protected void flowThrough(ConstantState in, Unit d, ConstantState out) { Object rop = assign.getRightOp(); Constant value = null; if (rop instanceof Constant) { - value = (Constant) rop; + //Class Constants can trigger a NoClassDefFoundError. + //Therefore, cannot not propagate them in some cases, since we might change the semantics of the original + //program w.r.t. traps. + //The normal constant propagator propagates them when they are safe to propagate. Implementing this here is harder, + //since we need to keep track of trap handlers at all assigns in the original code. + if (!(rop instanceof ClassConstant)) { + value = (Constant) rop; + } } else { if (rop instanceof Local) { value = in.getConstant((Local) rop); From 9c7db53571d82f07991ba3648a282a3db28fa2df Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Tue, 14 Jan 2025 16:07:38 +0100 Subject: [PATCH 2/2] Stylecheck --- .../soot/toolkits/scalar/FlowSensitiveConstantPropagator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java b/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java index 6a1e8842c83..f82591f478e 100644 --- a/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java +++ b/src/main/java/soot/toolkits/scalar/FlowSensitiveConstantPropagator.java @@ -348,7 +348,8 @@ protected void flowThrough(ConstantState in, Unit d, ConstantState out) { //Class Constants can trigger a NoClassDefFoundError. //Therefore, cannot not propagate them in some cases, since we might change the semantics of the original //program w.r.t. traps. - //The normal constant propagator propagates them when they are safe to propagate. Implementing this here is harder, + //The normal constant propagator propagates them when they are safe to propagate. + //Implementing this here is harder, //since we need to keep track of trap handlers at all assigns in the original code. if (!(rop instanceof ClassConstant)) { value = (Constant) rop;