From 2059bdc17dbf04a0dfdd5d7237b8a3eaa01fc38b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:11:52 +0000 Subject: [PATCH 1/2] Bump soot from 4.2.1 to 4.4.1 Bumps [soot](https://github.com/soot-oss/soot) from 4.2.1 to 4.4.1. - [Release notes](https://github.com/soot-oss/soot/releases) - [Changelog](https://github.com/soot-oss/soot/blob/develop/CHANGES) - [Commits](https://github.com/soot-oss/soot/commits) --- updated-dependencies: - dependency-name: org.soot-oss:soot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- CryptoAnalysis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index c2f47a594..36a35eda3 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -39,7 +39,7 @@ 2.5.1 - 4.2.1 + 4.4.1 From 3d80bc576fb302b957c9770e68206709677213dd Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Mon, 19 Feb 2024 10:48:33 +0100 Subject: [PATCH 2/2] Add transformer architecture to deal with new features from Soot 4.4.1 --- CryptoAnalysis/pom.xml | 1 - .../java/crypto/HeadlessCryptoScanner.java | 22 ++++-- .../crypto/preanalysis/CastTransformer.java | 68 +++++++++++++++++++ .../ExceptionAwareTransformer.java | 25 ++----- .../crypto/preanalysis/PreTransformer.java | 43 ++++++++++++ .../crypto/preanalysis/TransformerSetup.java | 48 +++++++++++++ .../typestate/CrySLMethodToSootMethod.java | 6 +- .../test/UsagePatternTestingFramework.java | 48 +++++++------ .../headless/BouncyCastleHeadlessTest.java | 6 +- 9 files changed, 215 insertions(+), 52 deletions(-) create mode 100644 CryptoAnalysis/src/main/java/crypto/preanalysis/CastTransformer.java create mode 100644 CryptoAnalysis/src/main/java/crypto/preanalysis/PreTransformer.java create mode 100644 CryptoAnalysis/src/main/java/crypto/preanalysis/TransformerSetup.java diff --git a/CryptoAnalysis/pom.xml b/CryptoAnalysis/pom.xml index ed969cb04..18bb9d4ae 100644 --- a/CryptoAnalysis/pom.xml +++ b/CryptoAnalysis/pom.xml @@ -308,7 +308,6 @@ org.bouncycastle bcprov-jdk18on 1.77 - test diff --git a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java index 695bad2e8..748b99f98 100644 --- a/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java +++ b/CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java @@ -4,7 +4,6 @@ import boomerang.callgraph.ObservableICFG; import boomerang.debugger.Debugger; import boomerang.debugger.IDEVizDebugger; -import boomerang.preanalysis.BoomerangPretransformer; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import crypto.analysis.CrySLAnalysisListener; @@ -16,8 +15,8 @@ import crypto.analysis.IAnalysisSeed; import crypto.exceptions.CryptoAnalysisException; import crypto.exceptions.CryptoAnalysisParserException; -import crypto.preanalysis.ExceptionAwareTransformer; import crypto.preanalysis.SeedFactory; +import crypto.preanalysis.TransformerSetup; import crypto.providerdetection.ProviderDetection; import crypto.reporting.CSVReporter; import crypto.reporting.CSVSummaryReporter; @@ -138,7 +137,7 @@ public void exec() { } catch (CryptoAnalysisException e) { LOGGER.error("Error happened when executing HeadlessCryptoScanner.", e); } - ExceptionAwareTransformer.setup(rules); + LOGGER.info("Analysis soot setup done in {} ", stopwatch); analyse(); LOGGER.info("Analysis finished in {}", stopwatch); @@ -189,8 +188,8 @@ private Transformer createAnalysisTransformer() { @Override protected void internalTransform(String phaseName, Map options) { - BoomerangPretransformer.v().reset(); - BoomerangPretransformer.v().apply(); + TransformerSetup.v().setupPreTransformer(rules); + ObservableDynamicICFG observableDynamicICFG = new ObservableDynamicICFG(false); List rules = HeadlessCryptoScanner.rules; @@ -336,6 +335,14 @@ private void initializeSootWithEntryPointAllReachable(boolean wholeProgram) thro Options.v().set_no_bodies_for_excluded(true); Options.v().set_allow_phantom_refs(true); Options.v().set_keep_line_number(true); + + /* This phase is new in soot 4.3.0 and manipulates the jimple code in a + * way that CryptoAnalysis is not able to find seeds in some cases (see + * https://github.com/CROSSINGTUD/CryptoAnalysis/issues/293). Therefore, + * it is disabled. + */ + Options.v().setPhaseOption("jb.sils", "enabled:false"); + // JAVA 8 if(getJavaVersion() < 9) { @@ -380,13 +387,14 @@ private List getIncludeList() { includeList.add("java.lang.String"); includeList.add("java.lang.StringCoding"); includeList.add("java.lang.StringIndexOutOfBoundsException"); - return includeList; + return new LinkedList<>(); } private List getExcludeList() { List exList = new LinkedList(); List rules = getRules(); - for(CrySLRule r : rules) { + + for (CrySLRule r : rules) { exList.add(r.getClassName()); } return exList; diff --git a/CryptoAnalysis/src/main/java/crypto/preanalysis/CastTransformer.java b/CryptoAnalysis/src/main/java/crypto/preanalysis/CastTransformer.java new file mode 100644 index 000000000..383322d10 --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/preanalysis/CastTransformer.java @@ -0,0 +1,68 @@ +package crypto.preanalysis; + +import soot.Body; +import soot.UnitPatchingChain; +import soot.Value; +import soot.jimple.AssignStmt; +import soot.jimple.CastExpr; +import soot.jimple.Constant; + +import java.util.Map; + +/** + * This transformer removes explicit cast expressions from the jimple code. + * Since Soot 4.3.0, it transforms the expression 'int a = 65000' into the jimple + * statement '$v = (int) 65000'. However, Boomerang is not able to extract the + * value 65000 from the statement because of the explicit cast operation (int). + * For the analysis, this operation is irrelevant because the variables always + * have the correct type (i.e. $v is of type int), so we can remove the cast + * operation. + */ +public class CastTransformer extends PreTransformer { + + private static CastTransformer instance; + + public CastTransformer() { + super(); + } + + @Override + protected void internalTransform(Body body, String phaseName, Map options) { + if (!body.getMethod().getDeclaringClass().isApplicationClass()) { + return; + } + + final UnitPatchingChain units = body.getUnits(); + units.snapshotIterator().forEachRemaining(unit -> { + if (!(unit instanceof AssignStmt)) { + return; + } + AssignStmt assignStmt = (AssignStmt) unit; + + Value rightSide = assignStmt.getRightOp(); + if (!(rightSide instanceof CastExpr)) { + return; + } + + CastExpr castExpr = (CastExpr) rightSide; + if (!(castExpr.getOp() instanceof Constant)) { + return; + } + Constant constant = (Constant) castExpr.getOp(); + + assignStmt.setRightOp(constant); + }); + } + + public static CastTransformer v() { + if (instance == null) { + instance = new CastTransformer(); + } + return instance; + } + + public void reset() { + instance = null; + } + +} diff --git a/CryptoAnalysis/src/main/java/crypto/preanalysis/ExceptionAwareTransformer.java b/CryptoAnalysis/src/main/java/crypto/preanalysis/ExceptionAwareTransformer.java index bf1552c1e..9f20a650d 100644 --- a/CryptoAnalysis/src/main/java/crypto/preanalysis/ExceptionAwareTransformer.java +++ b/CryptoAnalysis/src/main/java/crypto/preanalysis/ExceptionAwareTransformer.java @@ -1,26 +1,16 @@ package crypto.preanalysis; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; - import crypto.constraints.ExceptionConstraint; import crypto.rules.CrySLExceptionConstraint; import crypto.rules.CrySLRule; import crypto.typestate.CrySLMethodToSootMethod; import crypto.typestate.LabeledMatcherTransition; import soot.Body; -import soot.BodyTransformer; -import soot.PackManager; -import soot.PhaseOptions; import soot.Scene; import soot.SootClass; import soot.SootMethod; -import soot.Transform; import soot.Unit; import soot.UnitPatchingChain; import soot.jimple.NullConstant; @@ -28,21 +18,16 @@ import soot.jimple.internal.JEqExpr; import soot.jimple.internal.JIfStmt; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + /** * This transformer adds a branch after each statement, that may throw an * Exception, to the handler of that Exception. * The exceptions that a statement may throw are declared in the CrySLRule. */ -public class ExceptionAwareTransformer extends BodyTransformer { - - public static void setup(final List rules) { - for (final CrySLRule rule : rules) { - final String phaseName = "jap.etr-" + rule.getClassName(); - PackManager.v().getPack("jap").remove(phaseName); - PackManager.v().getPack("jap").add(new Transform(phaseName, new ExceptionAwareTransformer(rule))); - PhaseOptions.v().setPhaseOption(phaseName, "on"); - } - } +public class ExceptionAwareTransformer extends PreTransformer { private final SootClass spec; diff --git a/CryptoAnalysis/src/main/java/crypto/preanalysis/PreTransformer.java b/CryptoAnalysis/src/main/java/crypto/preanalysis/PreTransformer.java new file mode 100644 index 000000000..9422db3a2 --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/preanalysis/PreTransformer.java @@ -0,0 +1,43 @@ +package crypto.preanalysis; + +import soot.BodyTransformer; +import soot.MethodOrMethodContext; +import soot.Scene; +import soot.SootMethod; +import soot.jimple.toolkits.callgraph.ReachableMethods; +import soot.util.queue.QueueReader; + +import java.util.HashMap; + +public abstract class PreTransformer extends BodyTransformer { + + private boolean applied; + + public PreTransformer() { + applied = false; + } + + public final void apply() { + if (isApplied()) { + return; + } + + /* Following the concept of Soot, each transformer is applied to each method + * body individually. It may be more efficient to apply all transformers at once + * to a method body. + */ + ReachableMethods reachableMethods = Scene.v().getReachableMethods(); + QueueReader listener = reachableMethods.listener(); + while (listener.hasNext()) { + SootMethod method = listener.next().method(); + if (method.hasActiveBody()) { + internalTransform(method.getActiveBody(), "preTrans", new HashMap<>()); + } + } + applied = true; + } + + public boolean isApplied() { + return applied; + } +} diff --git a/CryptoAnalysis/src/main/java/crypto/preanalysis/TransformerSetup.java b/CryptoAnalysis/src/main/java/crypto/preanalysis/TransformerSetup.java new file mode 100644 index 000000000..385aec318 --- /dev/null +++ b/CryptoAnalysis/src/main/java/crypto/preanalysis/TransformerSetup.java @@ -0,0 +1,48 @@ +package crypto.preanalysis; + +import boomerang.preanalysis.BoomerangPretransformer; +import crypto.rules.CrySLRule; + +import java.util.List; + +public class TransformerSetup { + + private static TransformerSetup instance; + + public void setupPreTransformer(List rules) { + // Transformer related to the analysis + setupCastTransformer(); + setupExceptionAwareTransformer(rules); + + // Transformer related to Boomerang + setupBoomerangTransformer(); + } + + public void setupCastTransformer() { + CastTransformer.v().reset(); + CastTransformer.v().apply(); + } + + public void setupExceptionAwareTransformer(List rules) { + for (CrySLRule rule : rules) { + ExceptionAwareTransformer transformer = new ExceptionAwareTransformer(rule); + transformer.apply(); + } + } + + public void setupBoomerangTransformer() { + BoomerangPretransformer.v().reset(); + BoomerangPretransformer.v().apply(); + } + + public static TransformerSetup v() { + if (instance == null) { + instance = new TransformerSetup(); + } + return instance; + } + + public void reset() { + instance = null; + } +} diff --git a/CryptoAnalysis/src/main/java/crypto/typestate/CrySLMethodToSootMethod.java b/CryptoAnalysis/src/main/java/crypto/typestate/CrySLMethodToSootMethod.java index 2b7e5a2c4..41ab75c37 100644 --- a/CryptoAnalysis/src/main/java/crypto/typestate/CrySLMethodToSootMethod.java +++ b/CryptoAnalysis/src/main/java/crypto/typestate/CrySLMethodToSootMethod.java @@ -74,8 +74,12 @@ private Collection _convert(CrySLMethod label) { for(SootClass c : classes) { for (SootMethod m : c.getMethods()) { if (m.getName().equals(methodNameWithoutDeclaringClass) && m.getParameterCount() == noOfParams) { - if (parametersMatch(label.getParameters(), m.getParameterTypes())){ + if (parametersMatch(label.getParameters(), m.getParameterTypes())) { res.add(m); + + // Set the class defining the target method to phantom to avoid analyzing the method, + // if it is defined in a superclass + c.setPhantomClass(); } } } diff --git a/CryptoAnalysis/src/test/java/test/UsagePatternTestingFramework.java b/CryptoAnalysis/src/test/java/test/UsagePatternTestingFramework.java index 4113a939c..d58c41dfb 100644 --- a/CryptoAnalysis/src/test/java/test/UsagePatternTestingFramework.java +++ b/CryptoAnalysis/src/test/java/test/UsagePatternTestingFramework.java @@ -1,28 +1,18 @@ package test; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import com.google.common.base.Joiner; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; -import com.google.common.collect.Table; -import com.google.common.collect.Table.Cell; - import boomerang.BackwardQuery; import boomerang.Query; import boomerang.callgraph.ObservableDynamicICFG; import boomerang.callgraph.ObservableICFG; import boomerang.jimple.Statement; import boomerang.jimple.Val; -import boomerang.preanalysis.BoomerangPretransformer; import boomerang.results.ForwardBoomerangResults; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import com.google.common.collect.Table; +import com.google.common.collect.Table.Cell; import crypto.analysis.AnalysisSeedWithSpecification; import crypto.analysis.CrySLAnalysisListener; import crypto.analysis.CrySLResultsReporter; @@ -48,6 +38,7 @@ import crypto.extractparameter.CallSiteWithParamIndex; import crypto.extractparameter.ExtractedValue; import crypto.interfaces.ISLConstraint; +import crypto.preanalysis.TransformerSetup; import crypto.rules.CrySLPredicate; import crypto.rules.CrySLRule; import soot.Body; @@ -81,19 +72,31 @@ import test.core.selfrunning.ImprecisionException; import typestate.TransitionFunction; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + public abstract class UsagePatternTestingFramework extends AbstractTestingFramework{ protected ObservableICFG icfg; private JimpleBasedInterproceduralCFG staticIcfg; - List rules; + private List rules = getRules(); @Override protected SceneTransformer createAnalysisTransformer() throws ImprecisionException { + + // Required since Soot 4.3.0 + Options.v().setPhaseOption("jb.sils", "enabled:false"); + return new SceneTransformer() { - protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes") Map options) { - BoomerangPretransformer.v().reset(); - BoomerangPretransformer.v().apply(); + protected void internalTransform(String phaseName, Map options) { + TransformerSetup.v().setupPreTransformer(rules); + staticIcfg = new JimpleBasedInterproceduralCFG(true); // icfg = new ObservableStaticICFG(new JimpleBasedInterproceduralCFG(true)); icfg = new ObservableDynamicICFG(true); @@ -348,7 +351,7 @@ public void addProgress(int processedSeeds, int workListsize) { return reporters; } }; - scanner.scan(getRules()); + scanner.scan(rules); List unsound = Lists.newLinkedList(); List imprecise = Lists.newLinkedList(); @@ -389,7 +392,8 @@ private List getRules() { @Override public List excludedPackages() { List excludedPackages = super.excludedPackages(); - for(CrySLRule r : getRules()) { + + for (CrySLRule r : rules) { excludedPackages.add(r.getClassName()); } return excludedPackages; diff --git a/CryptoAnalysis/src/test/java/tests/headless/BouncyCastleHeadlessTest.java b/CryptoAnalysis/src/test/java/tests/headless/BouncyCastleHeadlessTest.java index 0bf91fab3..142d4aac3 100644 --- a/CryptoAnalysis/src/test/java/tests/headless/BouncyCastleHeadlessTest.java +++ b/CryptoAnalysis/src/test/java/tests/headless/BouncyCastleHeadlessTest.java @@ -40,6 +40,10 @@ public void testBCMacExamples() { setErrorsCount("", RequiredPredicateError.class, 1); setErrorsCount("", RequiredPredicateError.class, 3); + // Class from BouncyCastle should not be analyzed + //setErrorsCount("", RequiredPredicateError.class, 1); + setErrorsCount(RequiredPredicateError.class, new FindingsType.FalsePositives(1, "Analysis should not go into BouncyCastle class"), ""); + scanner.exec(); assertErrors(); } @@ -108,7 +112,7 @@ public void testBCDigestExamples() { setErrorsCount("", RequiredPredicateError.class, 1); setErrorsCount("", TypestateError.class, 2); - setErrorsCount("", TypestateError.class, 3); + setErrorsCount("", TypestateError.class, 4); scanner.exec(); assertErrors();