From 388f97893dc897337cc2a22095d9e3a29e8e7a63 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Thu, 26 Mar 2026 16:42:58 +0100 Subject: [PATCH 01/18] Introduce value support --- .../schema/SourcesAndSinks.xsd | 25 +++ .../xml/AbstractXMLSourceSinkParser.java | 92 ++++++++-- .../source/parsers/xml/XMLConstants.java | 5 + .../parsers/xml/XMLSourceSinkParser.java | 6 +- soot-infoflow-cmd/schema/SourcesAndSinks.xsd | 37 ++++ .../SummaryTaintPropagationHandler.java | 3 +- .../methodSummary/xml/SummaryReader.java | 4 +- .../summariesManual/android.net.Uri.xml | 8 + soot-infoflow/pom.xml | 6 - .../jimple/infoflow/AbstractInfoflow.java | 36 ++-- .../WideningTaintPropagationHandler.java | 3 +- .../jimple/infoflow/data/ValueOnPath.java | 107 +++++++++++ .../SequentialTaintPropagationHandler.java | 5 +- .../handlers/TaintPropagationHandler.java | 36 ++-- .../problems/AbstractInfoflowProblem.java | 12 +- .../problems/BackwardsInfoflowProblem.java | 11 ++ .../infoflow/problems/InfoflowProblem.java | 10 ++ .../rules/forward/SinkPropagationRule.java | 23 ++- .../river/ConditionalFlowPostProcessor.java | 2 +- ...nditionalFlowSourceSinkManagerWrapper.java | 95 +++++----- .../ConditionalSecondarySourceDefinition.java | 4 - .../river/IConditionalFlowManager.java | 69 +++++--- .../river/SecondaryFlowGenerator.java | 18 +- .../infoflow/river/SecondaryFlowListener.java | 3 +- .../river/SecondarySinkDefinition.java | 4 - .../river/TurnAroundFlowGenerator.java | 144 +++++++++++++++ .../TurnAroundSecondarySinkDefinition.java | 12 ++ .../TurnAroundSecondarySourceDefinition.java | 30 ++++ .../conditions/SignatureFlowCondition.java | 166 +++++++++++++++--- .../AbstractSourceSinkDefinition.java | 15 ++ .../definitions/ISourceSinkDefinition.java | 22 ++- .../MethodSourceSinkDefinition.java | 1 + .../definitions/SourceSinkCondition.java | 4 +- .../manager/BaseSourceSinkManager.java | 24 +++ .../manager/ISourceSinkManager.java | 56 ++++-- ...ugFlowFunctionTaintPropagationHandler.java | 3 +- .../jimple/infoflow/test/junit/HeapTests.java | 23 ++- 37 files changed, 908 insertions(+), 216 deletions(-) create mode 100644 soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySinkDefinition.java create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySourceDefinition.java diff --git a/soot-infoflow-android/schema/SourcesAndSinks.xsd b/soot-infoflow-android/schema/SourcesAndSinks.xsd index b71d54ae2..c3f217e2f 100644 --- a/soot-infoflow-android/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-android/schema/SourcesAndSinks.xsd @@ -96,6 +96,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -109,6 +132,8 @@ + + diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java index 34a37f8e7..874a446b3 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java @@ -24,6 +24,8 @@ import soot.jimple.infoflow.android.data.AndroidMethod; import soot.jimple.infoflow.android.data.CategoryDefinition; import soot.jimple.infoflow.data.AbstractMethodAndClass; +import soot.jimple.infoflow.data.ValueOnPath; +import soot.jimple.infoflow.data.ValueOnPath.Parameter; import soot.jimple.infoflow.river.conditions.SignatureFlowCondition; import soot.jimple.infoflow.sourcesSinks.definitions.AccessPathTuple; import soot.jimple.infoflow.sourcesSinks.definitions.FieldSourceSinkDefinition; @@ -112,11 +114,15 @@ protected class SAXHandler extends DefaultHandler { protected ICategoryFilter categoryFilter = null; + private Set turnAroundPaths; private Set signaturesOnPath = new HashSet<>(); private Set classNamesOnPath = new HashSet<>(); + private Set valuesOnPath = new HashSet<>(); private Set excludedClassNames = new HashSet<>(); private Set conditions = new HashSet<>(); + private ValueOnPath vop; + private Parameter param; public SAXHandler() { } @@ -177,10 +183,18 @@ public void startElement(String uri, String localName, String qName, Attributes handleStarttagSignatureOnPath(attributes); break; + case XMLConstants.VALUE_ON_PATH_TAG: + handleStarttagValueOnPath(attributes); + break; + case XMLConstants.CLASS_NAME_ON_PATH_TAG: handleStarttagClassNameOnPath(attributes); break; + case XMLConstants.TURN_AROUND_TAG: + handleStarttagTurnAround(attributes); + break; + case XMLConstants.EXCLUDE_CLASS_NAME_TAG: handleStarttagExcludeClassName(attributes); break; @@ -254,6 +268,22 @@ protected void handleStarttagAccesspath(Attributes attributes) { } protected void handleStarttagParam(Attributes attributes, String qNameLower) { + if (vop != null) { + String tempStr = attributes.getValue(XMLConstants.INDEX_ATTRIBUTE); + if (tempStr != null && !tempStr.isEmpty()) + paramIndex = Integer.parseInt(tempStr); + tempStr = attributes.getValue(XMLConstants.REGEX_ATTRIBUTE); + boolean regex = false; + if (tempStr != null && !tempStr.isEmpty()) + regex = Boolean.parseBoolean(tempStr); + tempStr = attributes.getValue(XMLConstants.CASE_SENSITIVE_ATTRIBUTE); + boolean casesensitive = false; + if (tempStr != null && !tempStr.isEmpty()) + casesensitive = Boolean.parseBoolean(tempStr); + param = new ValueOnPath.Parameter(paramIndex, regex, casesensitive); + vop.add(param); + return; + } if ((methodSignature != null || fieldSignature != null) && attributes != null) { String tempStr = attributes.getValue(XMLConstants.INDEX_ATTRIBUTE); if (tempStr != null && !tempStr.isEmpty()) @@ -290,6 +320,25 @@ protected void handleStarttagSignatureOnPath(Attributes attributes) { } } + protected void handleStarttagValueOnPath(Attributes attributes) { + String invocation = getStringAttribute(attributes, XMLConstants.INVOCATION_ATTRIBUTE); + if (invocation != null) { + if (valuesOnPath == null) + valuesOnPath = new HashSet<>(); + vop = new ValueOnPath("<" + invocation + ">"); + valuesOnPath.add(vop); + } + } + + protected void handleStarttagTurnAround(Attributes attributes) { + String invocationName = getStringAttribute(attributes, XMLConstants.INVOCATION_ATTRIBUTE); + if (invocationName != null) { + if (turnAroundPaths == null) + turnAroundPaths = new HashSet<>(); + turnAroundPaths.add(invocationName); + } + } + protected void handleStarttagClassNameOnPath(Attributes attributes) { String className = getStringAttribute(attributes, XMLConstants.CLASS_NAME_ATTRIBUTE); if (className != null) { @@ -331,6 +380,10 @@ private String parseSignature(Attributes attributes) { **/ @Override public void characters(char[] ch, int start, int length) throws SAXException { + if (param != null) { + param.setContentToMatch(new String(ch, start, length)); + param = null; + } } /** @@ -391,15 +444,18 @@ public void endElement(String uri, String localName, String qName) throws SAXExc accessPathParentElement = ""; paramIndex = -1; paramTypes.clear(); + vop = null; + param = null; break; case XMLConstants.ADDITIONAL_FLOW_CONDITION_TAG: - if (!classNamesOnPath.isEmpty() || !signaturesOnPath.isEmpty()) { + if (!classNamesOnPath.isEmpty() || !signaturesOnPath.isEmpty() || !valuesOnPath.isEmpty()) { SignatureFlowCondition additionalFlowCondition = new SignatureFlowCondition(classNamesOnPath, - signaturesOnPath, excludedClassNames); + signaturesOnPath, valuesOnPath, excludedClassNames); // Reset both for a new condition classNamesOnPath = new HashSet<>(); signaturesOnPath = new HashSet<>(); + valuesOnPath = new HashSet<>(); excludedClassNames = new HashSet<>(); @@ -422,7 +478,8 @@ protected void handleEndtagMethod() { if (tempMeth != null) { @SuppressWarnings("unchecked") ISourceSinkDefinition ssd = createMethodSourceSinkDefinition(tempMeth, baseAPs, - paramAPs.toArray(new Set[paramAPs.size()]), returnAPs, callType, category, conditions); + paramAPs.toArray(new Set[paramAPs.size()]), returnAPs, callType, category, conditions, + turnAroundPaths); addSourceSinkDefinition(methodSignature, ssd); } else { logger.error("Invalid method signature: " + methodSignature); @@ -431,6 +488,7 @@ protected void handleEndtagMethod() { } // Start a new method and discard our old data + turnAroundPaths = null; methodSignature = null; fieldSignature = null; baseAPs = new HashSet<>(); @@ -666,23 +724,25 @@ protected abstract ISourceSinkDefinition createMethodSourceSinkDefinition(Abstra /** * Factory method for {@link MethodSourceSinkDefinition} instances * - * @param method The method that is to be defined as a source or sink - * @param baseAPs The access paths rooted in the base object that shall be - * considered as sources or sinks - * @param paramAPs The access paths rooted in parameters that shall be - * considered as sources or sinks. The index in the set - * corresponds to the index of the formal parameter to which - * the respective set of access paths belongs. - * @param returnAPs The access paths rooted in the return object that shall be - * considered as sources or sinks - * @param callType The type of call (normal call, callback, etc.) - * @param conditions Conditions which has to be true for the definition to be - * valid + * @param method The method that is to be defined as a source or sink + * @param baseAPs The access paths rooted in the base object that shall + * be considered as sources or sinks + * @param paramAPs The access paths rooted in parameters that shall be + * considered as sources or sinks. The index in the set + * corresponds to the index of the formal parameter to + * which the respective set of access paths belongs. + * @param returnAPs The access paths rooted in the return object that + * shall be considered as sources or sinks + * @param callType The type of call (normal call, callback, etc.) + * @param conditions Conditions which has to be true for the definition to + * be valid + * @param turnAroundPaths a set of turn around path method invocations * @return The newly created {@link MethodSourceSinkDefinition} instance */ protected abstract ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodAndClass method, Set baseAPs, Set[] paramAPs, Set returnAPs, - CallType callType, ISourceSinkCategory category, Set conditions); + CallType callType, ISourceSinkCategory category, Set conditions, + Set turnAroundPaths); /** * Reads the method or field signature from the given attribute map diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java index b85af5c66..422d1cafb 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java @@ -18,13 +18,16 @@ public class XMLConstants { public static final String ACCESSPATH_TAG = "accesspath"; public static final String PATHELEMENT_TAG = "pathelement"; public static final String ADDITIONAL_FLOW_CONDITION_TAG = "additionalflowcondition"; + public static final String TURN_AROUND_TAG = "turnaround"; public static final String SIGNATURE_ON_PATH_TAG = "signatureonpath"; + public static final String VALUE_ON_PATH_TAG = "valueonpath"; public static final String CLASS_NAME_ON_PATH_TAG = "classnameonpath"; public static final String CLASS_NAME_ATTRIBUTE = "className"; public static final String EXCLUDE_CLASS_NAME_TAG = "excludeclassname"; public static final String ID_ATTRIBUTE = "id"; public static final String SIGNATURE_ATTRIBUTE = "signature"; + public static final String INVOCATION_ATTRIBUTE = "invocation"; public static final String CALL_TYPE = "callType"; public static final String TYPE_ATTRIBUTE = "type"; public static final String INDEX_ATTRIBUTE = "index"; @@ -33,6 +36,8 @@ public class XMLConstants { public static final String LENGTH_ATTRIBUTE = "length"; public static final String FIELD_ATTRIBUTE = "field"; public static final String DESCRIPTION_ATTRIBUTE = "description"; + public static final String REGEX_ATTRIBUTE = "regex"; + public static final String CASE_SENSITIVE_ATTRIBUTE = "caseSensitive"; public static final String TRUE = "true"; diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java index 62e5ba3ef..d92fc2df0 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java @@ -212,13 +212,17 @@ protected ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodA return null; } + @Override protected ISourceSinkDefinition createMethodSourceSinkDefinition(AbstractMethodAndClass method, Set baseAPs, Set[] paramAPs, Set returnAPs, - CallType callType, ISourceSinkCategory category, Set conditions) { + CallType callType, ISourceSinkCategory category, Set conditions, + Set turnAround) { ISourceSinkDefinition ssdef = createMethodSourceSinkDefinition(method, baseAPs, paramAPs, returnAPs, callType, category); if (ssdef != null) ssdef.setConditions(conditions); + if (turnAround != null) + ssdef.setTurnArounds(turnAround); return ssdef; } diff --git a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd index 6e7719332..c3f217e2f 100644 --- a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd @@ -50,6 +50,7 @@ + @@ -95,6 +96,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -108,9 +132,22 @@ + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/handler/SummaryTaintPropagationHandler.java b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/handler/SummaryTaintPropagationHandler.java index f47f49e19..39bde6ff4 100644 --- a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/handler/SummaryTaintPropagationHandler.java +++ b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/handler/SummaryTaintPropagationHandler.java @@ -16,6 +16,7 @@ import soot.jimple.infoflow.handlers.TaintPropagationHandler; import soot.jimple.infoflow.methodSummary.generator.gaps.GapManager; import soot.jimple.infoflow.methodSummary.generator.gaps.IGapManager; +import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.solver.cfg.IInfoflowCFG; import soot.util.ConcurrentHashMultiMap; import soot.util.MultiMap; @@ -201,7 +202,7 @@ protected void addResult(Abstraction abs, Stmt stmt) { @Override public boolean notifyFlowOut(Unit u, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { // Do not propagate through excluded methods SootMethod sm = manager.getICFG().getMethodOf(u); if (excludedMethods.contains(sm)) { diff --git a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java index a1a4353e7..5fbffd800 100644 --- a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java +++ b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java @@ -57,7 +57,7 @@ private enum State { } /** - * It takes quite a while to create a new XML Input Factory + * It takes quite a while to create a new XML Input Factory */ private static class CachedFactory { WeakReference thread; @@ -93,7 +93,7 @@ public void read(Reader reader, ClassMethodSummaries summaries) throws XMLStreamException, SummaryXMLException, IOException { XMLStreamReader xmlreader = null; try { - //Sadly, the XML Input Factory is not thread safe :/ + // Sadly, the XML Input Factory is not thread safe :/ CachedFactory cachedFact = cachedFactory; if (cachedFact == null || !cachedFact.isValidForThisThread()) { cachedFact = new CachedFactory(); diff --git a/soot-infoflow-summaries/summariesManual/android.net.Uri.xml b/soot-infoflow-summaries/summariesManual/android.net.Uri.xml index 8a13d2e74..82fc3e268 100644 --- a/soot-infoflow-summaries/summariesManual/android.net.Uri.xml +++ b/soot-infoflow-summaries/summariesManual/android.net.Uri.xml @@ -323,5 +323,13 @@ + + + + + + + + \ No newline at end of file diff --git a/soot-infoflow/pom.xml b/soot-infoflow/pom.xml index 496a2afbc..9bb94e131 100644 --- a/soot-infoflow/pom.xml +++ b/soot-infoflow/pom.xml @@ -157,12 +157,6 @@ 7.0.2 test - - io.agentscope - agentscope - 1.0.9 - compile - diff --git a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java index 09f2dba07..a75338f1f 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java +++ b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java @@ -126,6 +126,7 @@ import soot.jimple.infoflow.river.IUsageContextProvider; import soot.jimple.infoflow.river.SecondaryFlowGenerator; import soot.jimple.infoflow.river.SecondaryFlowListener; +import soot.jimple.infoflow.river.TurnAroundFlowGenerator; import soot.jimple.infoflow.solver.DefaultSolverPeerGroup; import soot.jimple.infoflow.solver.IInfoflowSolver; import soot.jimple.infoflow.solver.ISolverPeerGroup; @@ -667,11 +668,14 @@ private static List patchStringConcatInstruction(Stmt callSite, DynamicInv while (uses.hasNext()) { Value lop = assign.getLeftOp(); if (uses.next().getValue() == lop) { - //Since FlowDroid doesn't support tracking the taint over this statement, we have a problem: - //e.g. - //tainted = dynamicinvoke "makeConcatWithConstants" (tainted, tainted2) ... - //this would erroneously clear the taint on tainted - //to avoid that, we introduce an alias before that statement and use that instead for our concatenation. + // Since FlowDroid doesn't support tracking the taint over this statement, we + // have a problem: + // e.g. + // tainted = dynamicinvoke "makeConcatWithConstants" (tainted, tainted2) ... + // this would erroneously clear the taint on tainted + // to avoid that, we introduce an alias before that statement and use that + // instead for our concatenation. Local alias = lg.generateLocal(lop.getType()); Body body = callSite.getContainingBody(); AssignStmt assignAlias = Jimple.v().newAssignStmt(alias, lop); @@ -964,8 +968,8 @@ protected void unsplitAllBodies() { for (SootClass sc : Scene.v().getClasses()) { for (SootMethod m : sc.getMethods()) { if (m.hasActiveBody()) { - //We could use the local packer here, but we know exactly what was being split - //so we can be faster here + // We could use the local packer here, but we know exactly what was being split + // so we can be faster here Body body = m.getActiveBody(); Iterator it = body.getUseAndDefBoxesIterator(); while (it.hasNext()) { @@ -987,9 +991,9 @@ protected void unsplitAllBodies() { } } - //With newer soot versions, locals are reused more often, which - //can be a problem for FlowDroid. So, we split the locals prior to - //running FlowDroid. + // With newer soot versions, locals are reused more often, which + // can be a problem for FlowDroid. So, we split the locals prior to + // running FlowDroid. protected void splitAllBodies(Iterator it) { FlowDroidLocalSplitter splitter = getLocalSplitter(); while (it.hasNext()) { @@ -1132,6 +1136,8 @@ public Thread newThread(Runnable r) { if (config.getAdditionalFlowsEnabled()) { // Add the SecondaryFlowGenerator to the main forward taint analysis TaintPropagationHandler forwardHandler = forwardProblem.getTaintPropagationHandler(); + // TODO: + forwardHandler = new SecondaryFlowListener(); if (forwardHandler != null) { if (forwardHandler instanceof SequentialTaintPropagationHandler) { ((SequentialTaintPropagationHandler) forwardHandler).addHandler(new SecondaryFlowGenerator()); @@ -1165,7 +1171,10 @@ public Thread newThread(Runnable r) { memoryWatcher.addSolver((IMemoryBoundedSolver) additionalSolver); // Set all handlers to the additional problem - additionalProblem.setTaintPropagationHandler(new SecondaryFlowListener()); + SequentialTaintPropagationHandler seqTpg = new SequentialTaintPropagationHandler(); + seqTpg.addHandler(new SecondaryFlowListener()); + seqTpg.addHandler(new TurnAroundFlowGenerator(forwardSolver)); + additionalProblem.setTaintPropagationHandler(seqTpg); additionalProblem.setTaintWrapper(taintWrapper); additionalNativeCallHandler = new BackwardNativeCallHandler(); additionalProblem.setNativeCallHandler(additionalNativeCallHandler); @@ -1891,6 +1900,11 @@ private int scanMethodForSourcesSinks(final ISourceSinkManager sourcesSinks, Abs s.addTag(FlowDroidSinkStatement.INSTANCE); if (getConfig().getLogSourcesAndSinks()) collectedSinks.add(s); + for (ISourceSinkDefinition def : sos.sinkInfo.getDefinitions()) { + if (def.getTurnArounds() != null) { + sourcesSinks.addTurnArounds(def.getTurnArounds()); + } + } sinkCount++; break; case BOTH: diff --git a/soot-infoflow/src/soot/jimple/infoflow/collections/strategies/widening/WideningTaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/collections/strategies/widening/WideningTaintPropagationHandler.java index 6629e7260..6c3cd1ecf 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/collections/strategies/widening/WideningTaintPropagationHandler.java +++ b/soot-infoflow/src/soot/jimple/infoflow/collections/strategies/widening/WideningTaintPropagationHandler.java @@ -10,6 +10,7 @@ import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; /** * Widening through a taint propagation handler. Because of the nature, a full @@ -40,7 +41,7 @@ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, @Override public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { if (type != FlowFunctionType.CallToReturnFlowFunction) return false; diff --git a/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java new file mode 100644 index 000000000..bda6b0d2f --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java @@ -0,0 +1,107 @@ +package soot.jimple.infoflow.data; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Pattern; + +public class ValueOnPath { + + public static class Parameter { + + private int paramIdx; + private boolean regex, casesensitive; + private String value; + private Pattern matcher; + + public Parameter(int paramIndex, boolean regex, boolean casesensitive) { + this.paramIdx = paramIndex; + this.regex = regex; + this.casesensitive = casesensitive; + } + + public int getParameterIndex() { + return paramIdx; + } + + public boolean isRegex() { + return regex; + } + + public boolean isCaseSensitive() { + return casesensitive; + } + + @Override + public int hashCode() { + return Objects.hash(casesensitive, paramIdx, regex, value); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Parameter other = (Parameter) obj; + return casesensitive == other.casesensitive && paramIdx == other.paramIdx && regex == other.regex + && Objects.equals(value, other.value); + } + + public void setContentToMatch(String str) { + this.value = str; + } + + public String getContentToMatch() { + return value; + } + + public Pattern getRegexMatcher() { + if (matcher == null) + matcher = Pattern.compile(value, isCaseSensitive() ? 0 : Pattern.CASE_INSENSITIVE); + return matcher; + } + + } + + private String invocation; + private Set parameters; + + public ValueOnPath(String inv) { + this.invocation = inv; + } + + public String getInvocation() { + return invocation; + } + + public void add(Parameter parameter) { + if (parameters == null) + parameters = new HashSet<>(); + parameters.add(parameter); + } + + public Set getParameters() { + return parameters; + } + + @Override + public int hashCode() { + return Objects.hash(invocation, parameters); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ValueOnPath other = (ValueOnPath) obj; + return Objects.equals(invocation, other.invocation) && Objects.equals(parameters, other.parameters); + } + +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java index f64cbd586..68c2a718b 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java +++ b/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java @@ -7,6 +7,7 @@ import soot.Unit; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.problems.TaintPropagationResults; /** * Taint propagation handler that processes a sequence of inner handlers. For @@ -63,13 +64,13 @@ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, @Override public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { if (innerHandlers.isEmpty()) return false; boolean killed = false; for (TaintPropagationHandler handler : innerHandlers) { - if (handler.notifyFlowOut(stmt, d1, incoming, outgoing, manager, type)) + if (handler.notifyFlowOut(stmt, d1, incoming, outgoing, manager, results, type)) killed = true; } return killed; diff --git a/soot-infoflow/src/soot/jimple/infoflow/handlers/TaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/handlers/TaintPropagationHandler.java index a3e981fc3..f1ac720ea 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/handlers/TaintPropagationHandler.java +++ b/soot-infoflow/src/soot/jimple/infoflow/handlers/TaintPropagationHandler.java @@ -5,6 +5,7 @@ import soot.Unit; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.problems.TaintPropagationResults; /** * Handler interface for callbacks during taint propagation @@ -25,14 +26,10 @@ public enum FlowFunctionType { * Handler function that is invoked when a taint is proagated in the data flow * engine * - * @param stmt - * The statement over which the taint is propagated - * @param taint - * The taint being propagated - * @param manager - * The manager object that gives access to the data flow engine - * @param type - * The type of data flow edge being processed + * @param stmt The statement over which the taint is propagated + * @param taint The taint being propagated + * @param manager The manager object that gives access to the data flow engine + * @param type The type of data flow edge being processed */ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, FlowFunctionType type); @@ -40,22 +37,17 @@ public enum FlowFunctionType { * Handler function that is invoked when a new taint is generated in the data * flow engine * - * @param stmt - * The statement over which the taint is propagated - * @param d1 - * The abstraction at the beginning of the current method - * @param incoming - * The original abstraction from which the outgoing ones were - * computed - * @param outgoing - * The set of taints being propagated - * @param manager - * The manager object that gives access to the data flow engine - * @param type - * The type of data flow edge being processed + * @param stmt The statement over which the taint is propagated + * @param d1 The abstraction at the beginning of the current method + * @param incoming The original abstraction from which the outgoing ones were + * computed + * @param outgoing The set of taints being propagated + * @param manager The manager object that gives access to the data flow engine + * @param results The results object + * @param type The type of data flow edge being processed * @return Whether to kill the outgoing set */ public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type); + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java index 07b1fc387..4917b85af 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java @@ -320,7 +320,8 @@ protected boolean isExceptionHandler(Unit u) { protected Set notifyOutFlowHandlers(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, FlowFunctionType functionType) { if (taintPropagationHandler != null && outgoing != null && !outgoing.isEmpty()) { - boolean res = taintPropagationHandler.notifyFlowOut(stmt, d1, incoming, outgoing, manager, functionType); + boolean res = taintPropagationHandler.notifyFlowOut(stmt, d1, incoming, outgoing, manager, results, + functionType); if (res) return null; } @@ -387,10 +388,11 @@ public PropagationRuleManager getPropagationRules() { } /** - * Checks whether the arguments of a given invoke expression - * has a reference to a given base object while ignoring the given index - * @param e the invoke expr - * @param actualBase the base to look for + * Checks whether the arguments of a given invoke expression has a reference to + * a given base object while ignoring the given index + * + * @param e the invoke expr + * @param actualBase the base to look for * @param ignoreIndex the index to ignore * @return true if there is another reference */ diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java index 5aae89b97..2b62b6376 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java @@ -69,6 +69,13 @@ public BackwardsInfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } + private void report(Abstraction source, Unit src) { + Stmt s = (Stmt) src; + if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) + System.out.println("Backwards. " + source.toString() + ": " + src + " in " + + ((Stmt) src).getContainingBody().getMethod()); + } + @Override protected FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -94,6 +101,7 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, srcUnit); Set res = null; ByReferenceBoolean killSource = new ByReferenceBoolean(); ByReferenceBoolean killAll = new ByReferenceBoolean(); @@ -507,6 +515,7 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, callStmt); // Respect user settings if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -727,6 +736,7 @@ public Set computeTargets(Abstraction source, Abstraction calleeD1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { + report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -905,6 +915,7 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java index d98bfe448..402e6ee85 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java @@ -76,6 +76,12 @@ public InfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } + private void report(Abstraction source, Unit src) { + Stmt s = (Stmt) src; + if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) + System.out.println(source.toString() + ": " + src + " in " + ((Stmt) src).getContainingBody().getMethod()); + } + @Override public FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -366,6 +372,7 @@ public FlowFunction getNormalFlowFunction(final Unit src, final Uni @Override public Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, src); // Check whether we must activate a taint final Abstraction newSource; if (!source.isAbstractionActive() && src == source.getActivationUnit()) @@ -442,6 +449,7 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, src); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -525,6 +533,7 @@ public Set computeTargets(Abstraction source, Abstraction d1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { + report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -782,6 +791,7 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { + report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java index c4039b3cc..82f40f314 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java @@ -1,6 +1,7 @@ package soot.jimple.infoflow.problems.rules.forward; import java.util.Collection; +import java.util.Collections; import soot.SootMethod; import soot.Value; @@ -17,6 +18,8 @@ import soot.jimple.infoflow.data.AbstractionAtSink; import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; +import soot.jimple.infoflow.river.IAdditionalFlowSinkPropagationRule; +import soot.jimple.infoflow.river.SecondarySinkDefinition; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.util.BaseSelector; @@ -27,7 +30,7 @@ * * @author Steven Arzt */ -public class SinkPropagationRule extends AbstractTaintPropagationRule { +public class SinkPropagationRule extends AbstractTaintPropagationRule implements IAdditionalFlowSinkPropagationRule { private boolean killState = false; @@ -185,7 +188,8 @@ public Collection propagateReturnFlow(Collection calle /** * Registers a taint result - * @param sinkInfo information about the sink (must not be null) + * + * @param sinkInfo information about the sink (must not be null) * @param abstractionAtSink the abstraction at sink (must not be null) */ protected void registerTaintResult(SinkInfo sinkInfo, AbstractionAtSink abstractionAtSink) { @@ -198,4 +202,19 @@ protected void setKillState() { killState = true; } + @Override + public void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt) { + // Static fields are not part of the conditional flow model. + if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) + return; + + // Only proceed if stmt could influence the taint + if (!stmt.containsInvokeExpr() || !isTaintVisibleInCallee(stmt, source)) + return; + + getResults().addResult( + new AbstractionAtSink(Collections.singleton(SecondarySinkDefinition.INSTANCE), source, stmt)); + + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowPostProcessor.java b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowPostProcessor.java index c23747e59..7690dffc7 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowPostProcessor.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowPostProcessor.java @@ -43,7 +43,7 @@ public InfoflowResults onResultsAvailable(InfoflowResults results, IInfoflowCFG // One of the conditions must match. Within both, a class name and a signature // must match. if (conditions != null && !conditions.isEmpty() - && conditions.stream().noneMatch(cond -> cond.evaluate(dfRes, results))) + && conditions.stream().noneMatch(cond -> cond.evaluate(dfRes, results, manager))) tbr.add(dfRes.getSink()); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java index e38a79271..63e5be5a8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java @@ -5,64 +5,71 @@ import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.AccessPath; -import soot.jimple.infoflow.sourcesSinks.manager.*; +import soot.jimple.infoflow.sourcesSinks.manager.IReversibleSourceSinkManager; +import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; +import soot.jimple.infoflow.sourcesSinks.manager.SourceInfo; /** - * Wraps a SourceSinkManager and only redirects calls to methods of IConditionalFlowManager - * to the wrapped SourceSinkManager. - * Provide this to the Additional Flow such that it knows the conditions but doesn't accept sources. + * Wraps a SourceSinkManager and only redirects calls to methods of + * IConditionalFlowManager to the wrapped SourceSinkManager. Provide this to the + * Additional Flow such that it knows the conditions but doesn't accept sources. * * @author Tim Lange */ public class ConditionalFlowSourceSinkManagerWrapper implements IReversibleSourceSinkManager, IConditionalFlowManager { - private final IConditionalFlowManager inner; + private final IConditionalFlowManager inner; - public ConditionalFlowSourceSinkManagerWrapper(IConditionalFlowManager inner) { - this.inner = inner; - } + public ConditionalFlowSourceSinkManagerWrapper(IConditionalFlowManager inner) { + this.inner = inner; + } - @Override - public boolean isSecondarySink(Stmt stmt) { - return inner.isSecondarySink(stmt); - } + @Override + public boolean isSecondarySink(Stmt stmt) { + return inner.isSecondarySink(stmt); + } - @Override - public void registerSecondarySink(Stmt stmt) { - // NO-OP - } + @Override + public void registerSecondarySink(Stmt stmt) { + // NO-OP + } - @Override - public void registerSecondarySink(SootMethod sm) { - // NO-OP - } + @Override + public void registerSecondarySink(SootMethod sm) { + // NO-OP + } - @Override - public boolean isConditionalSink(Stmt stmt, SootClass baseClass) { - return inner.isConditionalSink(stmt, baseClass); - } + @Override + public boolean isConditionalSink(Stmt stmt, SootClass baseClass) { + return inner.isConditionalSink(stmt, baseClass); + } - @Override - public void initialize() { - // NO-OP - } + @Override + public boolean isTurnAroundPoint(SootMethod method) { + return inner.isTurnAroundPoint(method); + } - @Override - public SourceInfo getSourceInfo(Stmt sCallSite, InfoflowManager manager) { - return null; - } + @Override + public void initialize() { + // NO-OP + } - @Override - public SinkInfo getSinkInfo(Stmt sCallSite, InfoflowManager manager, AccessPath ap) { - return null; - } + @Override + public SourceInfo getSourceInfo(Stmt sCallSite, InfoflowManager manager) { + return null; + } - @Override - public SinkInfo getInverseSourceInfo(Stmt sCallSite, InfoflowManager manager, AccessPath ap) { - return null; - } + @Override + public SinkInfo getSinkInfo(Stmt sCallSite, InfoflowManager manager, AccessPath ap) { + return null; + } - @Override - public SourceInfo getInverseSinkInfo(Stmt sCallSite, InfoflowManager manager) { - return null; - } + @Override + public SinkInfo getInverseSourceInfo(Stmt sCallSite, InfoflowManager manager, AccessPath ap) { + return null; + } + + @Override + public SourceInfo getInverseSinkInfo(Stmt sCallSite, InfoflowManager manager) { + return null; + } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalSecondarySourceDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalSecondarySourceDefinition.java index 8c74bc8f9..fb4a68a02 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalSecondarySourceDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalSecondarySourceDefinition.java @@ -12,10 +12,6 @@ public class ConditionalSecondarySourceDefinition extends AbstractSourceSinkDefinition { public static ConditionalSecondarySourceDefinition INSTANCE = new ConditionalSecondarySourceDefinition(); - private ConditionalSecondarySourceDefinition() { - // - } - @Override public ISourceSinkDefinition getSourceOnlyDefinition() { return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java index e110bd6e0..8d6c3a2ca 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java @@ -5,41 +5,52 @@ import soot.jimple.Stmt; /** - * A source sink manager that is able to manage sinks with conditions, i.e. the sink context. + * A source sink manager that is able to manage sinks with conditions, i.e. the + * sink context. * * @author Tim Lange */ public interface IConditionalFlowManager { - /** - * Checks if a sink is reached in the secondary flow - * - * @param stmt Sink Statement - * @return true if stmt is a secondary sink - */ - boolean isSecondarySink(Stmt stmt); + /** + * Checks if a sink is reached in the secondary flow + * + * @param stmt Sink Statement + * @return true if stmt is a secondary sink + */ + boolean isSecondarySink(Stmt stmt); - /** - * Checks whether stmt is a conditional sink and needs a secondary flow. - * Ensures that the statement contains a InstanceInvokeExpr. - * - * @param stmt Sink Statement - * @param baseClass Class of the tainted base - * @return true if stmt is a conditional sink - */ - boolean isConditionalSink(Stmt stmt, SootClass baseClass); + /** + * Checks whether stmt is a conditional sink and needs a secondary flow. Ensures + * that the statement contains a InstanceInvokeExpr. + * + * @param stmt Sink Statement + * @param baseClass Class of the tainted base + * @return true if stmt is a conditional sink + */ + boolean isConditionalSink(Stmt stmt, SootClass baseClass); - /** - * Register a secondary sink at runtime. - * - * @param stmt Secondary sink statement - */ - void registerSecondarySink(Stmt stmt); + /** + * Register a secondary sink at runtime. + * + * @param stmt Secondary sink statement + */ + void registerSecondarySink(Stmt stmt); - /** - * Register a secondary sink at runtime. - * - * @param sm Secondary sink method - */ - void registerSecondarySink(SootMethod sm); + /** + * Register a secondary sink at runtime. + * + * @param sm Secondary sink method + */ + void registerSecondarySink(SootMethod sm); + + /** + * Checks whether a called soot method is considered a turnaround point by any + * sink + * + * @param method the method + * @return true when the called soot method is considered a turnaround point by + * any sink + */ + public boolean isTurnAroundPoint(SootMethod method); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java index aae6fdd0a..2c365268c 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java @@ -15,6 +15,7 @@ import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; @@ -56,7 +57,7 @@ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, @Override public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { // We only need to handle CallToReturn edges if (type != FlowFunctionType.CallToReturnFlowFunction) return false; @@ -92,6 +93,7 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Query the backward analysis for (Abstraction addAbs : additionalAbsSet) for (Unit pred : manager.getICFG().getPredsOf(unit)) + // from forwards to backwards analysis manager.additionalManager.getMainSolver().processEdge(new PathEdge<>(d1, pred, addAbs)); return false; } @@ -100,7 +102,7 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se * Creates a new abstraction that is injected into the backward direction. * * @param baseTaint Taint of the base local - * @param stmt Current statement + * @param stmt Current statement * @return New abstraction */ protected Abstraction createAdditionalFlowAbstraction(Abstraction baseTaint, Stmt stmt) { @@ -115,8 +117,8 @@ protected Abstraction createAdditionalFlowAbstraction(Abstraction baseTaint, Stm /** * Creates a new abstraction that is injected into the backward direction. * - * @param spec Flow Specification - * @param stmt Current statement + * @param spec Flow Specification + * @param stmt Current statement * @param manager Infoflow Manager * @return New abstraction */ @@ -131,10 +133,10 @@ protected Abstraction createAdditionalFlowAbstraction(AdditionalFlowInfoSpecific } /** - * Check whether baseLocal is tainted in the outgoing set. - * Assumes baseLocal is an object and the check happens at a call site. + * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is + * an object and the check happens at a call site. * - * @param outgoing outgoing taint set + * @param outgoing outgoing taint set * @param baseLocal base local * @return corresponding abstraction if baseLocal is tainted else null */ @@ -150,7 +152,7 @@ protected Abstraction getTaintFromLocal(Set outgoing, Value baseLoc * Check whether the access path is read at unit. * * @param unit unit - * @param ap access path + * @param ap access path * @return true if ap is read at unit */ protected boolean isReadAt(Unit unit, AccessPath ap) { diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java index 33d08f26d..af4409962 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java @@ -7,6 +7,7 @@ import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.problems.rules.ITaintPropagationRule; import soot.jimple.infoflow.problems.rules.PropagationRuleManager; @@ -61,7 +62,7 @@ public void notifyFlowIn(Unit unit, Abstraction incoming, InfoflowManager manage @Override public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { // NO-OP return false; } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondarySinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondarySinkDefinition.java index f4525c71e..ea55ebf0e 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondarySinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondarySinkDefinition.java @@ -17,10 +17,6 @@ public class SecondarySinkDefinition extends AbstractSourceSinkDefinition { public static final SecondarySinkDefinition INSTANCE = new SecondarySinkDefinition(); - private SecondarySinkDefinition() { - // - } - @Override public ISourceSinkDefinition getSourceOnlyDefinition() { return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java new file mode 100644 index 000000000..9a87b16df --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java @@ -0,0 +1,144 @@ +package soot.jimple.infoflow.river; + +import java.util.Collections; +import java.util.Set; + +import heros.solver.PathEdge; +import soot.RefType; +import soot.Unit; +import soot.Value; +import soot.ValueBox; +import soot.jimple.InstanceInvokeExpr; +import soot.jimple.Stmt; +import soot.jimple.infoflow.InfoflowManager; +import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.data.AbstractionAtSink; +import soot.jimple.infoflow.data.AccessPath; +import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; +import soot.jimple.infoflow.solver.IInfoflowSolver; +import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; + +/** + * TaintPropagationHandler querying the forward analysis when reaching a + * turnaround point. Attach to the backward analysis. + * + */ + +public class TurnAroundFlowGenerator implements TaintPropagationHandler { + // SourceSinkManager that also keeps track of conditions + private IConditionalFlowManager condFlowManager = null; + private IInfoflowSolver forwardSolver; + + public TurnAroundFlowGenerator(IInfoflowSolver forwardSolver) { + this.forwardSolver = forwardSolver; + } + + /** + * Ensures the condFlowManager field is always set. + * + * @param manager Infoflow Manager + */ + private void ensureCondFlowManager(InfoflowManager manager) { + if (condFlowManager != null) + return; + + if (!manager.getConfig().getAdditionalFlowsEnabled()) + throw new IllegalStateException("Additional flows are not enabled!"); + + ISourceSinkManager ssm = manager.getSourceSinkManager(); + if (ssm instanceof IConditionalFlowManager) { + condFlowManager = (IConditionalFlowManager) ssm; + return; + } + + throw new IllegalStateException("Additional Flows enabled but no ConditionalFlowManager in place!"); + } + + @Override + public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, FlowFunctionType type) { + // NO-OP + } + + @Override + public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Set outgoing, + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { + // We only need to handle CallToReturn edges + if (type != FlowFunctionType.CallToReturnFlowFunction) + return false; + + // Check whether any use matches the incoming taint + if (!isReadAt(unit, incoming.getAccessPath())) + return false; + + ensureCondFlowManager(manager); + + Stmt stmt = (Stmt) unit; + + // Check for sink contexts + if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { + Abstraction baseTaint = getTaintFromLocal(outgoing, ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); + + // Is the base tainted in the outgoing set? + if (baseTaint != null && baseTaint.getAccessPath().getBaseType() instanceof RefType) { + if (manager.getSourceSinkManager().isTurnAroundPoint(stmt.getInvokeExpr().getMethod())) { + Abstraction newAbs = createAdditionalFlowAbstraction(baseTaint, stmt); + // Query the forward analysis + forwardSolver.processEdge(new PathEdge<>(d1, unit, newAbs)); + + results.addResult(new AbstractionAtSink( + Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), incoming, stmt)); + } + } + } + + return false; + } + + /** + * Creates a new abstraction that is injected into the forward direction. + * + * @param baseTaint Taint of the base local + * @param stmt Current statement + * @return New abstraction + */ + protected Abstraction createAdditionalFlowAbstraction(Abstraction baseTaint, Stmt stmt) { + Abstraction newAbs = new Abstraction(Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), + baseTaint.getAccessPath(), stmt, null, false, false); + newAbs.setCorrespondingCallSite(stmt); + newAbs.setSourceContext(new AdditionalFlowInfoSourceContext(TurnAroundSecondarySinkDefinition.INSTANCE, + baseTaint.getAccessPath(), stmt)); + return newAbs.deriveNewAbstractionWithTurnUnit(stmt); + } + + /** + * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is + * an object and the check happens at a call site. + * + * @param outgoing outgoing taint set + * @param baseLocal base local + * @return corresponding abstraction if baseLocal is tainted else null + */ + protected Abstraction getTaintFromLocal(Set outgoing, Value baseLocal) { + for (Abstraction abs : outgoing) + if (abs.getAccessPath().getPlainValue() == baseLocal) + return abs; + + return null; + } + + /** + * Check whether the access path is read at unit. + * + * @param unit unit + * @param ap access path + * @return true if ap is read at unit + */ + protected boolean isReadAt(Unit unit, AccessPath ap) { + for (ValueBox box : unit.getUseBoxes()) + if (box.getValue() == ap.getPlainValue()) + return true; + + return false; + } +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySinkDefinition.java new file mode 100644 index 000000000..0ad14d94d --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySinkDefinition.java @@ -0,0 +1,12 @@ +package soot.jimple.infoflow.river; + +/** + * This is used as a sink to denote a connection from a primary flow sink to a + * turnaround point. + * + * @author Marc Miltenberger + */ +public class TurnAroundSecondarySinkDefinition extends SecondarySinkDefinition { + public static TurnAroundSecondarySinkDefinition INSTANCE = new TurnAroundSecondarySinkDefinition(); + +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySourceDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySourceDefinition.java new file mode 100644 index 000000000..5d06e34f2 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundSecondarySourceDefinition.java @@ -0,0 +1,30 @@ +package soot.jimple.infoflow.river; + +import soot.jimple.infoflow.sourcesSinks.definitions.AbstractSourceSinkDefinition; +import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; + +/** + * This is used as a source to denote a connection from a turnaround sink to a + * secondary sink. + * + * @author Marc Miltenberger + */ +public class TurnAroundSecondarySourceDefinition extends AbstractSourceSinkDefinition { + public static TurnAroundSecondarySourceDefinition INSTANCE = new TurnAroundSecondarySourceDefinition(); + + @Override + public ISourceSinkDefinition getSourceOnlyDefinition() { + return null; + } + + @Override + public ISourceSinkDefinition getSinkOnlyDefinition() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java index 7ba030f55..3cddc1a0d 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java @@ -1,7 +1,9 @@ package soot.jimple.infoflow.river.conditions; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -10,15 +12,26 @@ import soot.SootClass; import soot.SootMethod; import soot.Type; +import soot.Value; +import soot.jimple.ClassConstant; +import soot.jimple.Constant; +import soot.jimple.InvokeExpr; +import soot.jimple.NumericConstant; import soot.jimple.Stmt; +import soot.jimple.StringConstant; +import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.SootMethodAndClass; +import soot.jimple.infoflow.data.ValueOnPath; +import soot.jimple.infoflow.data.ValueOnPath.Parameter; import soot.jimple.infoflow.results.DataFlowResult; import soot.jimple.infoflow.results.InfoflowResults; import soot.jimple.infoflow.results.ResultSinkInfo; import soot.jimple.infoflow.results.ResultSourceInfo; import soot.jimple.infoflow.river.ConditionalSecondarySourceDefinition; +import soot.jimple.infoflow.river.TurnAroundSecondarySinkDefinition; import soot.jimple.infoflow.sourcesSinks.definitions.SourceSinkCondition; import soot.jimple.infoflow.util.SootMethodRepresentationParser; +import soot.util.HashMultiMap; import soot.util.MultiMap; /** @@ -37,25 +50,35 @@ public class SignatureFlowCondition extends SourceSinkCondition { private Set methodsOnPath = null; private Set classesOnPath = null; private Set excludedClasses = null; + private Set valuesOnPath = null; /** * Create a new additional flow condition * * @param classNamesOnPath class names that have to be on the path * @param signaturesOnPath signatures that have to be on the path + * @param valuesOnPath values that have to be on path * @param excludedClassNames class names of primary sinks that should be * filtered without context, e.g. * ByteArrayOutputStream for OutputStream */ public SignatureFlowCondition(Set classNamesOnPath, Set signaturesOnPath, - Set excludedClassNames) { + Set valuesOnPath, Set excludedClassNames) { this.classNamesOnPath = classNamesOnPath; this.signaturesOnPath = signaturesOnPath; + this.valuesOnPath = valuesOnPath; this.excludedClassNames = excludedClassNames; + if (valuesOnPath != null) { + for (ValueOnPath v : valuesOnPath) { + if (signaturesOnPath == null) + signaturesOnPath = new HashSet<>(); + signaturesOnPath.add(v.getInvocation()); + } + } } @Override - public boolean evaluate(DataFlowResult result, InfoflowResults results) { + public boolean evaluate(DataFlowResult result, InfoflowResults results, InfoflowManager manager) { // If we have nothing to check, we accept everything if (isEmpty()) return true; @@ -75,35 +98,28 @@ public boolean evaluate(DataFlowResult result, InfoflowResults results) { // Because we injected the taint in the SecondaryFlowGenerator with a // SecondarySinkDefinition, // if there is a flow containing the sink, it is always also in the MultiMap. - Pair, Set> flows = getSignaturesAndClassNamesReachedFromSink(additionalResults, sinkStmt); - ensureSootMethodsOnPath(); - boolean sigMatch = signaturesOnPath == null || signaturesOnPath.isEmpty() - || flows.getO1().stream().anyMatch(this::signatureMatches); - ensureSootClassesOnPath(); - boolean classMatch = classesOnPath == null || classesOnPath.isEmpty() - || flows.getO2().stream().anyMatch(c -> this.classMatches(c, classesOnPath)); - return sigMatch && classMatch; + return checkConditions(result, additionalResults, sinkStmt, manager); } - private boolean signatureMatches(String sig) { + private boolean signatureMatches(String sig, Set methodsCheck) { SootMethod sm = Scene.v().grabMethod(sig); if (sm == null) return false; - if (methodsOnPath.contains(sm)) + if (methodsCheck.contains(sm)) return true; for (SootClass ifc : sm.getDeclaringClass().getInterfaces()) { SootMethod superMethod = ifc.getMethodUnsafe(sm.getSubSignature()); - if (superMethod != null && methodsOnPath.contains(superMethod)) + if (superMethod != null && methodsCheck.contains(superMethod)) return true; } SootClass superClass = sm.getDeclaringClass().getSuperclassUnsafe(); while (superClass != null) { SootMethod superMethod = superClass.getMethodUnsafe(sm.getSubSignature()); - if (superMethod != null && methodsOnPath.contains(superMethod)) + if (superMethod != null && methodsCheck.contains(superMethod)) return true; superClass = superClass.getSuperclassUnsafe(); } @@ -225,29 +241,70 @@ private Set resolveSootClassesToSet(Set classNameSet) { } /** - * Retrieves the signatures and classes that can be reached from the primary - * sink/secondary source + * Checks the conditions + * + * @param result * * @param additionalResults MultiMap containing the additional results * @param primarySinkStmt Sink of interest - * @return A list of all callee signatures and a list of declaring classes on - * the path from the sink on + * @param manager + * @return true if the conditions matched */ - private Pair, Set> getSignaturesAndClassNamesReachedFromSink( - MultiMap additionalResults, Stmt primarySinkStmt) { + protected boolean checkConditions(DataFlowResult result, + MultiMap additionalResults, Stmt primarySinkStmt, + InfoflowManager manager) { Set sigSet = new HashSet<>(); Set classSet = new HashSet<>(); + MultiMap valueStmtMap = new HashMultiMap<>(); + List> turnAroundFlows = new ArrayList<>(); for (ResultSinkInfo secondarySinkInfo : additionalResults.keySet()) { for (ResultSourceInfo secondarySourceInfo : additionalResults.get(secondarySinkInfo)) { + if (secondarySourceInfo.getDefinition() instanceof TurnAroundSecondarySinkDefinition) { + turnAroundFlows.add(new Pair<>(secondarySourceInfo, secondarySinkInfo)); + } + + } + } + + for (ResultSinkInfo secondarySinkInfo : additionalResults.keySet()) { + for (ResultSourceInfo secondarySourceInfo : additionalResults.get(secondarySinkInfo)) { + if (!(secondarySourceInfo.getDefinition() instanceof ConditionalSecondarySourceDefinition)) + continue; // Match secondary source with primary sink of interest - if (secondarySourceInfo.getStmt() == primarySinkStmt - && secondarySourceInfo.getDefinition() instanceof ConditionalSecondarySourceDefinition) { + boolean matchesStmt = secondarySourceInfo.getStmt() == primarySinkStmt; + boolean hasTurnAround = false; + Stmt valueStmt = secondarySinkInfo.getStmt(); + if (!matchesStmt) { + hasTurnAround = secondarySinkInfo.getDefinition() instanceof TurnAroundSecondarySinkDefinition; + if (hasTurnAround) { + valueStmt = null; + Stmt stmt = secondarySinkInfo.getStmt(); + for (Pair ta : turnAroundFlows) { + ResultSourceInfo src = ta.getO1(); + if (src.getStmt() == stmt + && src.getAccessPath().equals(secondarySinkInfo.getAccessPath())) { + matchesStmt = true; + valueStmt = ta.getO2().getStmt(); + if (valueStmt.containsInvokeExpr()) { + SootMethod callee = valueStmt.getInvokeExpr().getMethod(); + sigSet.add(callee.getSignature()); + classSet.add(callee.getDeclaringClass().getName()); + } + break; + } + } + } + } + if (matchesStmt) { if (secondarySourceInfo.getPath() == null) { // Fall back if path reconstruction is not enabled SootMethod callee = secondarySinkInfo.getStmt().getInvokeExpr().getMethod(); sigSet.add(callee.getSignature()); classSet.add(callee.getDeclaringClass().getName()); + if (valueStmt != null) { + mapValueOnPath(valueStmtMap, valueStmt); + } } else { Stmt[] path = secondarySourceInfo.getPath(); for (Stmt stmt : path) { @@ -256,13 +313,76 @@ private Pair, Set> getSignaturesAndClassNamesReachedFromSink SootMethod callee = stmt.getInvokeExpr().getMethod(); sigSet.add(callee.getSignature()); classSet.add(callee.getDeclaringClass().getName()); + mapValueOnPath(valueStmtMap, stmt); } } } } } } - return new Pair<>(sigSet, classSet); + boolean sigMatch = signaturesOnPath == null || signaturesOnPath.isEmpty() + || sigSet.stream().anyMatch(c -> this.signatureMatches(c, methodsOnPath)); + boolean classMatch = classesOnPath == null || classesOnPath.isEmpty() + || classSet.stream().anyMatch(c -> this.classMatches(c, classesOnPath)); + boolean valuesMatch = valuesOnPath == null || valuesOnPath.isEmpty() + || valuesOnPath.stream().anyMatch(c -> this.valuesMatches(c, valueStmtMap.get(c))); + return sigMatch && classMatch && valuesMatch; + } + + private void mapValueOnPath(MultiMap results, Stmt stmt) { + if (valuesOnPath != null) { + for (ValueOnPath v : valuesOnPath) { + SootMethod m = Scene.v().grabMethod(v.getInvocation()); + + if (m != null && signatureMatches(stmt.getInvokeExpr().getMethod().getSignature(), + Collections.singleton(m))) { + results.put(v, stmt); + } + } + } + } + + private boolean valuesMatches(ValueOnPath c, Set stmts) { + nextStmt: for (Stmt s : stmts) { + InvokeExpr inv = s.getInvokeExpr(); + // we use AND on the parameters + for (Parameter p : c.getParameters()) { + int idx = p.getParameterIndex(); + if (idx < 0 || idx >= inv.getArgCount()) { + continue nextStmt; + } + Value v = inv.getArg(idx); + if (v instanceof Constant) { + String cmp; + if (v instanceof StringConstant) + cmp = ((StringConstant) v).value; + else if (v instanceof NumericConstant) + cmp = String.valueOf(((NumericConstant) v).getNumericValue()); + else if (v instanceof ClassConstant) + cmp = ((ClassConstant) v).getValue(); + else + continue nextStmt; + + String vopContent = p.getContentToMatch(); + + boolean matched = false; + if (p.isRegex()) { + matched = p.getRegexMatcher().matcher(cmp).matches(); + } else { + if (!p.isCaseSensitive()) { + matched = cmp.equalsIgnoreCase(vopContent); + } else { + matched = cmp.equals(vopContent); + } + } + if (!matched) + continue nextStmt; + } + } + // all matched + return true; + } + return false; } /** diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AbstractSourceSinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AbstractSourceSinkDefinition.java index 666403369..053bc2279 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AbstractSourceSinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AbstractSourceSinkDefinition.java @@ -13,6 +13,7 @@ public abstract class AbstractSourceSinkDefinition implements ISourceSinkDefinit protected ISourceSinkCategory category; protected Set conditions; + protected Set turnArounds; public AbstractSourceSinkDefinition() { } @@ -67,8 +68,22 @@ public boolean equals(Object obj) { if (other.conditions != null) return false; } else if (!conditions.equals(other.conditions)) + return false; + if (turnArounds == null) { + if (other.turnArounds != null) return false; + } else if (!turnArounds.equals(other.turnArounds)) + return false; return true; } + @Override + public void setTurnArounds(Set turnArounds) { + this.turnArounds = turnArounds; + } + + @Override + public Set getTurnArounds() { + return turnArounds; + } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java index ba1d9d210..4ff12510b 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/ISourceSinkDefinition.java @@ -58,10 +58,26 @@ public interface ISourceSinkDefinition { /** * Sets the conditions under which the source/sink definition is valid * - * @param conditions - * A set with the conditions under which the source/sink definition - * is valid, optionally null if no such conditions exist + * @param conditions A set with the conditions under which the source/sink + * definition is valid, optionally null if no + * such conditions exist */ public void setConditions(Set conditions); + /** + * Sets the turn around points where the flow direction for River flows is + * injected into a forward analysis + * + * @param turnAround the method signatures that indicate turn around points + */ + public void setTurnArounds(Set turnAround); + + /** + * Returns the turn around points where the flow direction for River flows is + * injected into a forward analysis + * + * @return returns the method signatures that indicate turn around points + */ + public Set getTurnArounds(); + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java index eb5777561..9b19a89d0 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java @@ -324,6 +324,7 @@ protected MethodSourceSinkDefinition buildNewDefinition(Set bas MethodSourceSinkDefinition def = buildNewDefinition(method, baseAPTs, paramAPTs, returnAPTs, callType); def.category = category; def.conditions = conditions; + def.turnArounds = turnArounds; return def; } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/SourceSinkCondition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/SourceSinkCondition.java index abec30387..ca3bb84b8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/SourceSinkCondition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/SourceSinkCondition.java @@ -7,6 +7,7 @@ import soot.Scene; import soot.SootClass; import soot.SootMethod; +import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.SootMethodAndClass; import soot.jimple.infoflow.results.DataFlowResult; import soot.jimple.infoflow.results.InfoflowResults; @@ -25,10 +26,11 @@ public abstract class SourceSinkCondition { * * @param result The data flow result * @param results All results of this data flow analysis + * @param manager The infoflow manager * @return True if the given data flow result matches the condition, otherwise * false */ - public abstract boolean evaluate(DataFlowResult result, InfoflowResults results); + public abstract boolean evaluate(DataFlowResult result, InfoflowResults results, InfoflowManager manager); /** * Gets all methods referenced by this condition diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java index df347680b..91a1892cf 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java @@ -161,6 +161,8 @@ public Collection load(SootClass sc) throws Exception { }); + private Set turnArounds; + /** * Creates a new instance of the {@link BaseSourceSinkManager} class with either * strong or weak matching. @@ -1154,4 +1156,26 @@ public boolean isConditionalSink(Stmt stmt, SootClass baseClass) { return false; } + + @Override + public boolean isTurnAroundPoint(SootMethod method) { + Set ta = this.turnArounds; + if (ta == null) + return false; + return ta.contains(method); + } + + @Override + public void addTurnArounds(Set turnArounds) { + if (this.turnArounds == null) { + this.turnArounds = new HashSet<>(turnArounds.size()); + } + for (String ta : turnArounds) { + SootMethod m = Scene.v().grabMethod("<" + ta + ">"); + if (m != null) { + this.turnArounds.add(m); + } + } + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ISourceSinkManager.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ISourceSinkManager.java index 7a6026194..92f9ecf85 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ISourceSinkManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ISourceSinkManager.java @@ -10,6 +10,9 @@ ******************************************************************************/ package soot.jimple.infoflow.sourcesSinks.manager; +import java.util.Set; + +import soot.SootMethod; import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.AccessPath; @@ -20,37 +23,52 @@ public interface ISourceSinkManager { /** - * Initialization method that is called after the Soot instance has been - * created and before the actual data flow tracking is started. + * Initialization method that is called after the Soot instance has been created + * and before the actual data flow tracking is started. */ public void initialize(); /** - * Determines if a method called by the Stmt is a source method or not. If - * so, additional information is returned + * Determines if a method called by the Stmt is a source method or not. If so, + * additional information is returned * - * @param sCallSite - * a Stmt which should include an invokeExrp calling a method - * @param manager - * The manager object for interacting with the solver - * @return A SourceInfo object containing additional information if this - * call is a source, otherwise null + * @param sCallSite a Stmt which should include an invokeExrp calling a method + * @param manager The manager object for interacting with the solver + * @return A SourceInfo object containing additional information if this call is + * a source, otherwise null */ public SourceInfo getSourceInfo(Stmt sCallSite, InfoflowManager manager); /** * Checks if the given access path at this statement will leak. * - * @param sCallSite - * The call site to check - * @param manager - * The manager object for interacting with the solver - * @param ap - * The access path to check. Pass null to check whether the given - * statement can be a sink for any given access path. - * @return A SinkInfo object containing additional information if this call - * is a sink, otherwise null + * @param sCallSite The call site to check + * @param manager The manager object for interacting with the solver + * @param ap The access path to check. Pass null to check whether the + * given statement can be a sink for any given access path. + * @return A SinkInfo object containing additional information if this call is a + * sink, otherwise null */ public SinkInfo getSinkInfo(Stmt sCallSite, InfoflowManager manager, AccessPath ap); + /** + * Checks whether a called soot method is considered a turnaround point by any + * sink + * + * @param method the method + * @return true when the called soot method is considered a turnaround point by + * any sink + */ + public default boolean isTurnAroundPoint(SootMethod method) { + return false; + } + + /** + * Adds turn arounds from a the given set + * + * @param turnArounds the turn around points + */ + public default void addTurnArounds(Set turnArounds) { + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/util/DebugFlowFunctionTaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/util/DebugFlowFunctionTaintPropagationHandler.java index bdc17f99a..5274e7d3d 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/util/DebugFlowFunctionTaintPropagationHandler.java +++ b/soot-infoflow/src/soot/jimple/infoflow/util/DebugFlowFunctionTaintPropagationHandler.java @@ -7,6 +7,7 @@ import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; /** * Prints all propagations to stdout. Useful to debug small test cases. @@ -56,7 +57,7 @@ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, @Override public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, - InfoflowManager manager, FlowFunctionType type) { + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { if (this.filter != null && !this.filter.evaluate(manager.getICFG().getMethodOf(stmt).toString())) return false; diff --git a/soot-infoflow/test/soot/jimple/infoflow/test/junit/HeapTests.java b/soot-infoflow/test/soot/jimple/infoflow/test/junit/HeapTests.java index dbff82201..dc51fb34a 100644 --- a/soot-infoflow/test/soot/jimple/infoflow/test/junit/HeapTests.java +++ b/soot-infoflow/test/soot/jimple/infoflow/test/junit/HeapTests.java @@ -20,7 +20,11 @@ import org.junit.Ignore; import org.junit.Test; -import soot.*; +import soot.RefType; +import soot.Scene; +import soot.SootField; +import soot.SootMethod; +import soot.Unit; import soot.jimple.AssignStmt; import soot.jimple.DefinitionStmt; import soot.jimple.InstanceInvokeExpr; @@ -36,6 +40,7 @@ import soot.jimple.infoflow.entryPointCreators.DefaultEntryPointCreator; import soot.jimple.infoflow.entryPointCreators.SequentialEntryPointCreator; import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.results.InfoflowResults; import soot.jimple.infoflow.sourcesSinks.definitions.MethodSourceSinkDefinition; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; @@ -1049,7 +1054,8 @@ public SourceInfo getSourceInfo(Stmt sCallSite, InfoflowManager manager) { if (sCallSite instanceof AssignStmt) { AssignStmt assignStmt = (AssignStmt) sCallSite; if (assignStmt.getRightOp().toString().contains("taintedBySourceSinkManager")) - return new SourceInfo(manager.getAccessPathFactory().createAccessPath(assignStmt.getLeftOp(), true)); + return new SourceInfo( + manager.getAccessPathFactory().createAccessPath(assignStmt.getLeftOp(), true)); else return null; } @@ -1279,7 +1285,6 @@ public void activationStatementTest1() { negativeCheckInfoflow(infoflow); } - @Test(timeout = 300000) public void callSiteCreatesAlias() { IInfoflow infoflow = initInfoflow(); @@ -1309,7 +1314,8 @@ public void notifyFlowIn(Unit stmt, Abstraction taint, InfoflowManager manager, } @Override - public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, InfoflowManager manager, FlowFunctionType type) { + public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { return false; } }); @@ -1318,7 +1324,6 @@ public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Se checkInfoflow(infoflow, 1); } - @Test(timeout = 300000) public void testRecursiveAccessPath() { IInfoflow infoflow = initInfoflow(); @@ -1339,8 +1344,8 @@ public void testRemoveEntailedAbstractions1() { epoints.add(""); infoflow.computeInfoflow(appPath, libPath, epoints, sources, sinks); checkInfoflow(infoflow, 1); - Assert.assertEquals(2, infoflow.getResults().getResultSet().stream() - .map(res -> res.getSource().getStmt()).distinct().count()); + Assert.assertEquals(2, + infoflow.getResults().getResultSet().stream().map(res -> res.getSource().getStmt()).distinct().count()); } @Test(timeout = 300000) @@ -1353,7 +1358,7 @@ public void testRemoveEntailedAbstractions2() { epoints.add(""); infoflow.computeInfoflow(appPath, libPath, epoints, sources, sinks); checkInfoflow(infoflow, 1); - Assert.assertEquals(2, infoflow.getResults().getResultSet().stream() - .map(res -> res.getSource().getStmt()).distinct().count()); + Assert.assertEquals(2, + infoflow.getResults().getResultSet().stream().map(res -> res.getSource().getStmt()).distinct().count()); } } From 3abe314b54e636ecae4767240a05d18cbe4c029b Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Thu, 26 Mar 2026 16:45:25 +0100 Subject: [PATCH 02/18] Refactorings --- .../river/SecondaryFlowGenerator.java | 37 ++------------- .../river/TurnAroundFlowGenerator.java | 38 +-------------- .../src/soot/jimple/infoflow/river/Utils.java | 46 +++++++++++++++++++ 3 files changed, 51 insertions(+), 70 deletions(-) create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/Utils.java diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java index 2c365268c..cef8b5820 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java @@ -7,8 +7,6 @@ import heros.solver.PathEdge; import soot.RefType; import soot.Unit; -import soot.Value; -import soot.ValueBox; import soot.jimple.InstanceInvokeExpr; import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; @@ -63,7 +61,7 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se return false; // Check whether any use matches the incoming taint - if (!isReadAt(unit, incoming.getAccessPath())) + if (!Utils.isReadAt(unit, incoming.getAccessPath())) return false; ensureCondFlowManager(manager); @@ -73,7 +71,8 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Check for sink contexts if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { - Abstraction baseTaint = getTaintFromLocal(outgoing, ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); + Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, + ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); // Is the base tainted in the outgoing set? if (baseTaint != null && baseTaint.getAccessPath().getBaseType() instanceof RefType) { @@ -132,34 +131,4 @@ protected Abstraction createAdditionalFlowAbstraction(AdditionalFlowInfoSpecific return newAbs.deriveNewAbstractionWithTurnUnit(stmt); } - /** - * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is - * an object and the check happens at a call site. - * - * @param outgoing outgoing taint set - * @param baseLocal base local - * @return corresponding abstraction if baseLocal is tainted else null - */ - protected Abstraction getTaintFromLocal(Set outgoing, Value baseLocal) { - for (Abstraction abs : outgoing) - if (abs.getAccessPath().getPlainValue() == baseLocal) - return abs; - - return null; - } - - /** - * Check whether the access path is read at unit. - * - * @param unit unit - * @param ap access path - * @return true if ap is read at unit - */ - protected boolean isReadAt(Unit unit, AccessPath ap) { - for (ValueBox box : unit.getUseBoxes()) - if (box.getValue() == ap.getPlainValue()) - return true; - - return false; - } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java index 9a87b16df..c3b981739 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java @@ -6,14 +6,11 @@ import heros.solver.PathEdge; import soot.RefType; import soot.Unit; -import soot.Value; -import soot.ValueBox; import soot.jimple.InstanceInvokeExpr; import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.data.AbstractionAtSink; -import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.handlers.TaintPropagationHandler; import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.solver.IInfoflowSolver; @@ -68,7 +65,7 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se return false; // Check whether any use matches the incoming taint - if (!isReadAt(unit, incoming.getAccessPath())) + if (!Utils.isReadAt(unit, incoming.getAccessPath())) return false; ensureCondFlowManager(manager); @@ -77,7 +74,7 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Check for sink contexts if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { - Abstraction baseTaint = getTaintFromLocal(outgoing, ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); + Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); // Is the base tainted in the outgoing set? if (baseTaint != null && baseTaint.getAccessPath().getBaseType() instanceof RefType) { @@ -110,35 +107,4 @@ protected Abstraction createAdditionalFlowAbstraction(Abstraction baseTaint, Stm baseTaint.getAccessPath(), stmt)); return newAbs.deriveNewAbstractionWithTurnUnit(stmt); } - - /** - * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is - * an object and the check happens at a call site. - * - * @param outgoing outgoing taint set - * @param baseLocal base local - * @return corresponding abstraction if baseLocal is tainted else null - */ - protected Abstraction getTaintFromLocal(Set outgoing, Value baseLocal) { - for (Abstraction abs : outgoing) - if (abs.getAccessPath().getPlainValue() == baseLocal) - return abs; - - return null; - } - - /** - * Check whether the access path is read at unit. - * - * @param unit unit - * @param ap access path - * @return true if ap is read at unit - */ - protected boolean isReadAt(Unit unit, AccessPath ap) { - for (ValueBox box : unit.getUseBoxes()) - if (box.getValue() == ap.getPlainValue()) - return true; - - return false; - } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java new file mode 100644 index 000000000..6c266a242 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java @@ -0,0 +1,46 @@ +package soot.jimple.infoflow.river; + +import java.util.Iterator; +import java.util.Set; + +import soot.Unit; +import soot.Value; +import soot.ValueBox; +import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.data.AccessPath; + +public class Utils { + + /** + * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is + * an object and the check happens at a call site. + * + * @param outgoing outgoing taint set + * @param baseLocal base local + * @return corresponding abstraction if baseLocal is tainted else null + */ + public static Abstraction getTaintFromLocal(Set outgoing, Value baseLocal) { + for (Abstraction abs : outgoing) + if (abs.getAccessPath().getPlainValue() == baseLocal) + return abs; + + return null; + } + + /** + * Check whether the access path is read at unit. + * + * @param unit unit + * @param ap access path + * @return true if ap is read at unit + */ + public static boolean isReadAt(Unit unit, AccessPath ap) { + Iterator it = unit.getUseBoxesIterator(); + while (it.hasNext()) + if (it.next().getValue() == ap.getPlainValue()) + return true; + + return false; + } + +} From 0d0968e8b92c8e9ae997c12de2f95a1cc74dda22 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Thu, 26 Mar 2026 17:17:39 +0100 Subject: [PATCH 03/18] Make AccessPathTypes optional and allow typing in the access path signature without brackets --- .../methodSummary/xml/SummaryReader.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java index 5fbffd800..a62cb3ceb 100644 --- a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java +++ b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java @@ -28,6 +28,7 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; +import soot.Scene; import soot.jimple.infoflow.collections.data.IndexConstraint; import soot.jimple.infoflow.collections.data.KeyConstraint; import soot.jimple.infoflow.methodSummary.data.sourceSink.ConstraintType; @@ -555,7 +556,10 @@ private String[] getAccessPath(Map attributes) { String ap = attributes.get(XMLConstants.ATTRIBUTE_ACCESSPATH); if (ap != null) { if (ap.length() > 3) { - String[] res = ap.substring(1, ap.length() - 1).split(","); + String apR = ap; + if (ap.startsWith("[") && ap.endsWith("]")) + apR = ap.substring(1, ap.length() - 1); + String[] res = apR.split(","); for (int i = 0; i < res.length; i++) { String curElement = res[i].trim(); @@ -578,13 +582,28 @@ private String[] getAccessPathTypes(Map attributes) { String ap = attributes.get(XMLConstants.ATTRIBUTE_ACCESSPATHTYPES); if (ap != null) { if (ap.length() > 3) { - String[] res = ap.substring(1, ap.length() - 1).split(","); + String apR = ap; + if (ap.startsWith("[") && ap.endsWith("]")) + apR = ap.substring(1, ap.length() - 1); + String[] res = apR.split(","); for (int i = 0; i < res.length; i++) res[i] = res[i].trim(); return res; } + return null; + } else { + // infer the access path types as default behavior + String[] a = getAccessPath(attributes); + if (a == null) + return null; + String[] res = new String[a.length]; + for (int i = 0; i < res.length; i++) { + String subsig = Scene.signatureToSubsignature(a[i]); + String fieldType = subsig.substring(0, subsig.indexOf(" ")); + res[i] = fieldType; + } + return res; } - return null; } private boolean isMatchStrict(Map attributes) { From 4ec4c053876641cfb943eeb10070c2b4b2f4aacf Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Fri, 27 Mar 2026 13:44:37 +0100 Subject: [PATCH 04/18] Remove debug statements --- .../infoflow/problems/BackwardsInfoflowProblem.java | 11 ----------- .../jimple/infoflow/problems/InfoflowProblem.java | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java index 2b62b6376..5aae89b97 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java @@ -69,13 +69,6 @@ public BackwardsInfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } - private void report(Abstraction source, Unit src) { - Stmt s = (Stmt) src; - if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) - System.out.println("Backwards. " + source.toString() + ": " + src + " in " - + ((Stmt) src).getContainingBody().getMethod()); - } - @Override protected FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -101,7 +94,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, srcUnit); Set res = null; ByReferenceBoolean killSource = new ByReferenceBoolean(); ByReferenceBoolean killAll = new ByReferenceBoolean(); @@ -515,7 +507,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, callStmt); // Respect user settings if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -736,7 +727,6 @@ public Set computeTargets(Abstraction source, Abstraction calleeD1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { - report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -915,7 +905,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java index 402e6ee85..d98bfe448 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java @@ -76,12 +76,6 @@ public InfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } - private void report(Abstraction source, Unit src) { - Stmt s = (Stmt) src; - if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) - System.out.println(source.toString() + ": " + src + " in " + ((Stmt) src).getContainingBody().getMethod()); - } - @Override public FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -372,7 +366,6 @@ public FlowFunction getNormalFlowFunction(final Unit src, final Uni @Override public Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, src); // Check whether we must activate a taint final Abstraction newSource; if (!source.isAbstractionActive() && src == source.getActivationUnit()) @@ -449,7 +442,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, src); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -533,7 +525,6 @@ public Set computeTargets(Abstraction source, Abstraction d1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { - report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -791,7 +782,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; From 0ba7dcdf1ded478b3dabdbaab66490a444985012 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Fri, 27 Mar 2026 13:44:37 +0100 Subject: [PATCH 05/18] Remove debug statements --- .../problems/BackwardsInfoflowProblem.java | 11 --- .../infoflow/problems/InfoflowProblem.java | 10 --- .../river/TurnAroundFlowListener.java | 72 +++++++++++++++++++ 3 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java index 2b62b6376..5aae89b97 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/BackwardsInfoflowProblem.java @@ -69,13 +69,6 @@ public BackwardsInfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } - private void report(Abstraction source, Unit src) { - Stmt s = (Stmt) src; - if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) - System.out.println("Backwards. " + source.toString() + ": " + src + " in " - + ((Stmt) src).getContainingBody().getMethod()); - } - @Override protected FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -101,7 +94,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, srcUnit); Set res = null; ByReferenceBoolean killSource = new ByReferenceBoolean(); ByReferenceBoolean killAll = new ByReferenceBoolean(); @@ -515,7 +507,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, callStmt); // Respect user settings if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -736,7 +727,6 @@ public Set computeTargets(Abstraction source, Abstraction calleeD1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { - report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; @@ -915,7 +905,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, callStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java index 402e6ee85..d98bfe448 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/InfoflowProblem.java @@ -76,12 +76,6 @@ public InfoflowProblem(InfoflowManager manager, Abstraction zeroValue, super(manager, zeroValue, ruleManagerFactory); } - private void report(Abstraction source, Unit src) { - Stmt s = (Stmt) src; - if (s.getContainingBody().getMethod().getName().toString().contains("testFP2")) - System.out.println(source.toString() + ": " + src + " in " + ((Stmt) src).getContainingBody().getMethod()); - } - @Override public FlowFunctions createFlowFunctionsFactory() { return new FlowFunctions() { @@ -372,7 +366,6 @@ public FlowFunction getNormalFlowFunction(final Unit src, final Uni @Override public Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, src); // Check whether we must activate a taint final Abstraction newSource; if (!source.isAbstractionActive() && src == source.getActivationUnit()) @@ -449,7 +442,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, src); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -533,7 +525,6 @@ public Set computeTargets(Abstraction source, Abstraction d1, private Set computeTargetsInternal(Abstraction source, Abstraction calleeD1, Collection callerD1s) { - report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; if (source == getZeroValue()) @@ -791,7 +782,6 @@ public Set computeTargets(Abstraction d1, Abstraction source) { } private Set computeTargetsInternal(Abstraction d1, Abstraction source) { - report(source, iCallStmt); if (manager.getConfig().getStopAfterFirstFlow() && !results.isEmpty()) return null; diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java new file mode 100644 index 000000000..863d0e6ce --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java @@ -0,0 +1,72 @@ +package soot.jimple.infoflow.river; + +import java.util.Set; + +import soot.Unit; +import soot.jimple.InvokeExpr; +import soot.jimple.Stmt; +import soot.jimple.infoflow.InfoflowManager; +import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.handlers.TaintPropagationHandler; +import soot.jimple.infoflow.problems.TaintPropagationResults; +import soot.jimple.infoflow.problems.rules.ITaintPropagationRule; +import soot.jimple.infoflow.problems.rules.PropagationRuleManager; + +/** + * TaintPropagationHandler to record which statements secondary flows reach. + * Attach to the backward analysis. + */ +public class TurnAroundFlowListener implements TaintPropagationHandler { + private IAdditionalFlowSinkPropagationRule sinkRule = null; + + /** + * Ensures that the field sourceRule is always set. + * + * @param manager + */ + private void ensureSourcePropagationRule(InfoflowManager manager) { + if (sinkRule != null) + return; + + if (!manager.getConfig().getAdditionalFlowsEnabled()) + throw new IllegalStateException("Additional flows are not enabled!"); + + PropagationRuleManager ruleManager = manager.getMainSolver().getTabulationProblem().getPropagationRules(); + for (ITaintPropagationRule rule : ruleManager.getRules()) { + if (rule instanceof IAdditionalFlowSinkPropagationRule) { + sinkRule = (IAdditionalFlowSinkPropagationRule) rule; + return; + } + } + + throw new IllegalStateException( + "Enabled additional flows but no IConditionalFlowSinkPropagationRule in place!"); + } + + @Override + public void notifyFlowIn(Unit unit, Abstraction incoming, InfoflowManager manager, FlowFunctionType type) { + if (type != FlowFunctionType.CallToReturnFlowFunction) + return; + + ensureSourcePropagationRule(manager); + if (!(manager.getSourceSinkManager() instanceof IConditionalFlowManager)) + return; + final IConditionalFlowManager ssm = (IConditionalFlowManager) manager.getSourceSinkManager(); + + Stmt stmt = (Stmt) unit; + InvokeExpr inv = stmt.getInvokeExprUnsafe(); + if (inv != null && ssm.isTurnAroundPoint(inv.getMethod())) { + // Record the statement + sinkRule.processTurnAroundSink(null, incoming, stmt); + } + + } + + @Override + public boolean notifyFlowOut(Unit stmt, Abstraction d1, Abstraction incoming, Set outgoing, + InfoflowManager manager, TaintPropagationResults results, FlowFunctionType type) { + // NO-OP + return false; + } + +} From 10b85bdd401387063611fdb7ed9328cd8a8132b4 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 30 Mar 2026 09:49:08 +0200 Subject: [PATCH 06/18] Intermediate commit --- .../org.asynchttpclient.Response.xml | 3 +-- .../jimple/infoflow/AbstractInfoflow.java | 19 +++++------------- .../SequentialTaintPropagationHandler.java | 13 ++++++++++++ .../BackwardsSourcePropagationRule.java | 16 +++++++++++---- .../rules/forward/SinkPropagationRule.java | 20 +------------------ .../IAdditionalFlowSinkPropagationRule.java | 18 +++++++++++++---- .../river/TurnAroundFlowGenerator.java | 6 ++---- .../river/TurnAroundFlowListener.java | 3 +++ 8 files changed, 51 insertions(+), 47 deletions(-) diff --git a/soot-infoflow-summaries/summariesManual/org.asynchttpclient.Response.xml b/soot-infoflow-summaries/summariesManual/org.asynchttpclient.Response.xml index 76f0f9a03..c9af11f26 100644 --- a/soot-infoflow-summaries/summariesManual/org.asynchttpclient.Response.xml +++ b/soot-infoflow-summaries/summariesManual/org.asynchttpclient.Response.xml @@ -5,8 +5,7 @@ + AccessPath="org.asynchttpclient.Response: byte[] bytes" /> diff --git a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java index a75338f1f..4ff1df3ca 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java +++ b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java @@ -127,6 +127,7 @@ import soot.jimple.infoflow.river.SecondaryFlowGenerator; import soot.jimple.infoflow.river.SecondaryFlowListener; import soot.jimple.infoflow.river.TurnAroundFlowGenerator; +import soot.jimple.infoflow.river.TurnAroundFlowListener; import soot.jimple.infoflow.solver.DefaultSolverPeerGroup; import soot.jimple.infoflow.solver.IInfoflowSolver; import soot.jimple.infoflow.solver.ISolverPeerGroup; @@ -1136,20 +1137,9 @@ public Thread newThread(Runnable r) { if (config.getAdditionalFlowsEnabled()) { // Add the SecondaryFlowGenerator to the main forward taint analysis TaintPropagationHandler forwardHandler = forwardProblem.getTaintPropagationHandler(); - // TODO: - forwardHandler = new SecondaryFlowListener(); - if (forwardHandler != null) { - if (forwardHandler instanceof SequentialTaintPropagationHandler) { - ((SequentialTaintPropagationHandler) forwardHandler).addHandler(new SecondaryFlowGenerator()); - } else { - SequentialTaintPropagationHandler seqTpg = new SequentialTaintPropagationHandler(); - seqTpg.addHandler(forwardHandler); - seqTpg.addHandler(new SecondaryFlowGenerator()); - forwardProblem.setTaintPropagationHandler(seqTpg); - } - } else { - forwardProblem.setTaintPropagationHandler(new SecondaryFlowGenerator()); - } + forwardHandler = SequentialTaintPropagationHandler.concat(forwardHandler, new SecondaryFlowGenerator()); + + forwardProblem.setTaintPropagationHandler(forwardHandler); if (!(manager.getSourceSinkManager() instanceof IConditionalFlowManager)) throw new IllegalStateException("Additional Flows enabled but no ConditionalFlowManager in place!"); @@ -1174,6 +1164,7 @@ public Thread newThread(Runnable r) { SequentialTaintPropagationHandler seqTpg = new SequentialTaintPropagationHandler(); seqTpg.addHandler(new SecondaryFlowListener()); seqTpg.addHandler(new TurnAroundFlowGenerator(forwardSolver)); + seqTpg.addHandler(new TurnAroundFlowListener()); additionalProblem.setTaintPropagationHandler(seqTpg); additionalProblem.setTaintWrapper(taintWrapper); additionalNativeCallHandler = new BackwardNativeCallHandler(); diff --git a/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java index 68c2a718b..c271d9f49 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java +++ b/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java @@ -88,4 +88,17 @@ public void addAllHandlers(TaintPropagationHandler[] handlers) { } } + public static TaintPropagationHandler concat(TaintPropagationHandler first, TaintPropagationHandler second) { + if (first instanceof SequentialTaintPropagationHandler) { + SequentialTaintPropagationHandler f = (SequentialTaintPropagationHandler) first; + f.addHandler(second); + return f; + } else { + SequentialTaintPropagationHandler seq = new SequentialTaintPropagationHandler(); + seq.addHandler(first); + seq.addHandler(second); + return seq; + } + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java index 21a0e13f3..fc2d888c3 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java @@ -21,6 +21,7 @@ import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; import soot.jimple.infoflow.river.IAdditionalFlowSinkPropagationRule; import soot.jimple.infoflow.river.SecondarySinkDefinition; +import soot.jimple.infoflow.river.TurnAroundSecondarySinkDefinition; import soot.jimple.infoflow.sourcesSinks.manager.IReversibleSourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.util.BaseSelector; @@ -146,10 +147,6 @@ protected boolean isTaintVisibleInCallee(Stmt stmt, Abstraction source) { public Collection propagateCallToReturnFlow(Abstraction d1, Abstraction source, Stmt stmt, ByReferenceBoolean killSource, ByReferenceBoolean killAll) { - if (stmt.toString().equals( - "r1 = virtualinvoke r0.()")) - System.out.println("x"); - if (!(manager.getSourceSinkManager() instanceof IReversibleSourceSinkManager)) return null; final IReversibleSourceSinkManager ssm = (IReversibleSourceSinkManager) manager.getSourceSinkManager(); @@ -212,4 +209,15 @@ public void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt st getResults().addResult( new AbstractionAtSink(Collections.singleton(SecondarySinkDefinition.INSTANCE), source, stmt)); } + + @Override + public void processTurnAroundSink(Abstraction d1, Abstraction source, Stmt stmt) { + + // Static fields are not part of the conditional flow model. + if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) + return; + + getResults().addResult( + new AbstractionAtSink(Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), source, stmt)); + } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java index 82f40f314..626d1ffe7 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/forward/SinkPropagationRule.java @@ -1,7 +1,6 @@ package soot.jimple.infoflow.problems.rules.forward; import java.util.Collection; -import java.util.Collections; import soot.SootMethod; import soot.Value; @@ -18,8 +17,6 @@ import soot.jimple.infoflow.data.AbstractionAtSink; import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; -import soot.jimple.infoflow.river.IAdditionalFlowSinkPropagationRule; -import soot.jimple.infoflow.river.SecondarySinkDefinition; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.util.BaseSelector; @@ -30,7 +27,7 @@ * * @author Steven Arzt */ -public class SinkPropagationRule extends AbstractTaintPropagationRule implements IAdditionalFlowSinkPropagationRule { +public class SinkPropagationRule extends AbstractTaintPropagationRule { private boolean killState = false; @@ -202,19 +199,4 @@ protected void setKillState() { killState = true; } - @Override - public void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt) { - // Static fields are not part of the conditional flow model. - if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) - return; - - // Only proceed if stmt could influence the taint - if (!stmt.containsInvokeExpr() || !isTaintVisibleInCallee(stmt, source)) - return; - - getResults().addResult( - new AbstractionAtSink(Collections.singleton(SecondarySinkDefinition.INSTANCE), source, stmt)); - - } - } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java index 61bbd6105..dab76d00d 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java @@ -5,19 +5,29 @@ /** * Interface for Backward SourcePropagationRules that support conditional flows. - * Note that in Secondary Flows the "source" is the sink of the primary flow and the - * "sinks" are the statements reached in between + * Note that in Secondary Flows the "source" is the sink of the primary flow and + * the "sinks" are the statements reached in between * * @author Tim Lange */ public interface IAdditionalFlowSinkPropagationRule { /** - * Records a secondary flow taint reaching a statement. - * Important: This method does not check whether the secondary flow should be recorded. + * Records a secondary flow taint reaching a statement. Important: This method + * does not check whether the secondary flow should be recorded. * * @param d1 The calling context * @param source The current taint abstraction * @param stmt The current statement, which is assumed to be a sink */ void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt); + + /** + * Records a secondary flow taint reaching a turn-around point. Important: This + * method does not check whether the secondary flow should be recorded. + * + * @param d1 The calling context + * @param source The current taint abstraction + * @param stmt The current statement, which is assumed to be a sink + */ + void processTurnAroundSink(Abstraction d1, Abstraction source, Stmt stmt); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java index c3b981739..2bc2698ae 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java @@ -10,7 +10,6 @@ import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.Abstraction; -import soot.jimple.infoflow.data.AbstractionAtSink; import soot.jimple.infoflow.handlers.TaintPropagationHandler; import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.solver.IInfoflowSolver; @@ -74,7 +73,8 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Check for sink contexts if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { - Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); + Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, + ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); // Is the base tainted in the outgoing set? if (baseTaint != null && baseTaint.getAccessPath().getBaseType() instanceof RefType) { @@ -83,8 +83,6 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Query the forward analysis forwardSolver.processEdge(new PathEdge<>(d1, unit, newAbs)); - results.addResult(new AbstractionAtSink( - Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), incoming, stmt)); } } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java index 863d0e6ce..54f84ceab 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java @@ -51,6 +51,9 @@ public void notifyFlowIn(Unit unit, Abstraction incoming, InfoflowManager manage ensureSourcePropagationRule(manager); if (!(manager.getSourceSinkManager() instanceof IConditionalFlowManager)) return; + if (!Utils.isReadAt(unit, incoming.getAccessPath())) + return; + final IConditionalFlowManager ssm = (IConditionalFlowManager) manager.getSourceSinkManager(); Stmt stmt = (Stmt) unit; From 7b93e346152745725e933481be5471609a6ea112 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 30 Mar 2026 10:41:53 +0200 Subject: [PATCH 07/18] Improve code quality --- .../jimple/infoflow/AbstractInfoflow.java | 1 + ...BackwardPropagationRuleManagerFactory.java | 3 + .../DefaultPropagationRuleManagerFactory.java | 3 + .../BackwardsSourcePropagationRule.java | 82 +---------------- .../BackwardNoSinkRuleManagerFactory.java | 1 + .../IAdditionalFlowSinkPropagationRule.java | 33 ------- .../infoflow/river/RiverPropagationRule.java | 87 +++++++++++++++++++ .../infoflow/river/SecondaryFlowListener.java | 6 +- .../river/TurnAroundFlowListener.java | 6 +- .../src/soot/jimple/infoflow/river/Utils.java | 42 +++++++++ 10 files changed, 146 insertions(+), 118 deletions(-) delete mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java create mode 100644 soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java diff --git a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java index 4ff1df3ca..2ad1b595a 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java +++ b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java @@ -1138,6 +1138,7 @@ public Thread newThread(Runnable r) { // Add the SecondaryFlowGenerator to the main forward taint analysis TaintPropagationHandler forwardHandler = forwardProblem.getTaintPropagationHandler(); forwardHandler = SequentialTaintPropagationHandler.concat(forwardHandler, new SecondaryFlowGenerator()); + forwardHandler = SequentialTaintPropagationHandler.concat(forwardHandler, new SecondaryFlowListener()); forwardProblem.setTaintPropagationHandler(forwardHandler); diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/BackwardPropagationRuleManagerFactory.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/BackwardPropagationRuleManagerFactory.java index 4a7223df7..3b771eee8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/BackwardPropagationRuleManagerFactory.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/BackwardPropagationRuleManagerFactory.java @@ -16,6 +16,7 @@ import soot.jimple.infoflow.problems.rules.backward.BackwardsWrapperRule; import soot.jimple.infoflow.problems.rules.forward.SkipSystemClassRule; import soot.jimple.infoflow.problems.rules.forward.StopAfterFirstKFlowsPropagationRule; +import soot.jimple.infoflow.river.RiverPropagationRule; /** * Backward implementation of the {@link IPropagationRuleManagerFactory} class @@ -49,6 +50,8 @@ public PropagationRuleManager createRuleManager(InfoflowManager manager, Abstrac if (manager.getConfig().getImplicitFlowMode().trackControlFlowDependencies()) ruleList.add(BackwardsImplicitFlowRule.class); + if (manager.getConfig().getAdditionalFlowsEnabled()) + ruleList.add(RiverPropagationRule.class); return new PropagationRuleManager(manager, zeroValue, results, ruleList); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/DefaultPropagationRuleManagerFactory.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/DefaultPropagationRuleManagerFactory.java index bcdc98fe7..aad56609f 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/DefaultPropagationRuleManagerFactory.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/DefaultPropagationRuleManagerFactory.java @@ -20,6 +20,7 @@ import soot.jimple.infoflow.problems.rules.forward.StrongUpdatePropagationRule; import soot.jimple.infoflow.problems.rules.forward.TypingPropagationRule; import soot.jimple.infoflow.problems.rules.forward.WrapperPropagationRule; +import soot.jimple.infoflow.river.RiverPropagationRule; /** * Default implementation of the {@link IPropagationRuleManagerFactory} class @@ -62,6 +63,8 @@ public PropagationRuleManager createRuleManager(InfoflowManager manager, Abstrac ruleList.add(SkipSystemClassRule.class); if (manager.getConfig().getStopAfterFirstKFlows() > 0) ruleList.add(StopAfterFirstKFlowsPropagationRule.class); + if (manager.getConfig().getAdditionalFlowsEnabled()) + ruleList.add(RiverPropagationRule.class); return new PropagationRuleManager(manager, zeroValue, results, ruleList); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java index fc2d888c3..7a94172eb 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/rules/backward/BackwardsSourcePropagationRule.java @@ -1,15 +1,12 @@ package soot.jimple.infoflow.problems.rules.backward; import java.util.Collection; -import java.util.Collections; import soot.SootMethod; import soot.Value; import soot.jimple.AssignStmt; import soot.jimple.IdentityStmt; import soot.jimple.IfStmt; -import soot.jimple.InstanceInvokeExpr; -import soot.jimple.InvokeExpr; import soot.jimple.LookupSwitchStmt; import soot.jimple.ReturnStmt; import soot.jimple.Stmt; @@ -19,9 +16,7 @@ import soot.jimple.infoflow.data.AbstractionAtSink; import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; -import soot.jimple.infoflow.river.IAdditionalFlowSinkPropagationRule; -import soot.jimple.infoflow.river.SecondarySinkDefinition; -import soot.jimple.infoflow.river.TurnAroundSecondarySinkDefinition; +import soot.jimple.infoflow.river.Utils; import soot.jimple.infoflow.sourcesSinks.manager.IReversibleSourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.util.BaseSelector; @@ -35,8 +30,7 @@ * @author Steven Arzt * @author Tim Lange */ -public class BackwardsSourcePropagationRule extends AbstractTaintPropagationRule - implements IAdditionalFlowSinkPropagationRule { +public class BackwardsSourcePropagationRule extends AbstractTaintPropagationRule { private boolean killState = false; @@ -106,43 +100,6 @@ public Collection propagateCallFlow(Abstraction d1, Abstraction sou return null; } - /** - * Checks whether the given taint is visible inside the method called at the - * given call site - * - * @param stmt A call site where a sink method is called - * @param source The taint that has arrived at the given statement - * @return True if the callee has access to the tainted value, false otherwise - */ - protected boolean isTaintVisibleInCallee(Stmt stmt, Abstraction source) { - InvokeExpr iexpr = stmt.getInvokeExpr(); - final Aliasing aliasing = getAliasing(); - - // Is an argument tainted? - final Value apBaseValue = source.getAccessPath().getPlainValue(); - if (apBaseValue != null && aliasing != null) { - for (int i = 0; i < iexpr.getArgCount(); i++) { - if (aliasing.mayAlias(iexpr.getArg(i), apBaseValue)) { - if (source.getAccessPath().getTaintSubFields() || source.getAccessPath().isLocal()) - return true; - } - } - } - - // Is the base object tainted? - if (iexpr instanceof InstanceInvokeExpr) { - if (((InstanceInvokeExpr) iexpr).getBase() == source.getAccessPath().getPlainValue()) - return true; - } - - // Is return tainted? - if (stmt instanceof AssignStmt && aliasing != null - && aliasing.mayAlias(apBaseValue, ((AssignStmt) stmt).getLeftOp())) - return true; - - return false; - } - @Override public Collection propagateCallToReturnFlow(Abstraction d1, Abstraction source, Stmt stmt, ByReferenceBoolean killSource, ByReferenceBoolean killAll) { @@ -155,7 +112,7 @@ public Collection propagateCallToReturnFlow(Abstraction d1, Abstrac if (source.isAbstractionActive() && !source.getAccessPath().isStaticFieldRef() && !source.getAccessPath().isEmpty()) { // Is the taint even visible inside the callee? - if (!stmt.containsInvokeExpr() || isTaintVisibleInCallee(stmt, source)) { + if (!stmt.containsInvokeExpr() || Utils.isTaintVisibleInCallee(stmt, source, getAliasing())) { // Get the sink descriptor SinkInfo sourceInfo = ssm.getInverseSourceInfo(stmt, getManager(), source.getAccessPath()); @@ -187,37 +144,4 @@ public Collection propagateReturnFlow(Collection calle return null; } - // Note: Do not get confused with on the terms source/sink. In the general case, - // the backward - // analysis starts the analysis at sinks and records results at the source. For - // secondary flows, - // the secondary source is equal to the primary sink and the secondary sink is - // an interesting - // statement (an additional flow condition or a usage context) at which we - // record a result. - // That's why the backward source rule is also the secondary flow sink rule. */ - @Override - public void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt) { - // Static fields are not part of the conditional flow model. - if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) - return; - - // Only proceed if stmt could influence the taint - if (!stmt.containsInvokeExpr() || !isTaintVisibleInCallee(stmt, source)) - return; - - getResults().addResult( - new AbstractionAtSink(Collections.singleton(SecondarySinkDefinition.INSTANCE), source, stmt)); - } - - @Override - public void processTurnAroundSink(Abstraction d1, Abstraction source, Stmt stmt) { - - // Static fields are not part of the conditional flow model. - if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) - return; - - getResults().addResult( - new AbstractionAtSink(Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), source, stmt)); - } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/BackwardNoSinkRuleManagerFactory.java b/soot-infoflow/src/soot/jimple/infoflow/river/BackwardNoSinkRuleManagerFactory.java index c3425b6a6..6d6064220 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/BackwardNoSinkRuleManagerFactory.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/BackwardNoSinkRuleManagerFactory.java @@ -50,6 +50,7 @@ public PropagationRuleManager createRuleManager(InfoflowManager manager, Abstrac if (manager.getConfig().getImplicitFlowMode().trackControlFlowDependencies()) ruleList.add(new BackwardsImplicitFlowRule()); + ruleList.add(new RiverPropagationRule()); return new PropagationRuleManager(manager, zeroValue, results, ruleList.toArray(new ITaintPropagationRule[0])); } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java deleted file mode 100644 index dab76d00d..000000000 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java +++ /dev/null @@ -1,33 +0,0 @@ -package soot.jimple.infoflow.river; - -import soot.jimple.Stmt; -import soot.jimple.infoflow.data.Abstraction; - -/** - * Interface for Backward SourcePropagationRules that support conditional flows. - * Note that in Secondary Flows the "source" is the sink of the primary flow and - * the "sinks" are the statements reached in between - * - * @author Tim Lange - */ -public interface IAdditionalFlowSinkPropagationRule { - /** - * Records a secondary flow taint reaching a statement. Important: This method - * does not check whether the secondary flow should be recorded. - * - * @param d1 The calling context - * @param source The current taint abstraction - * @param stmt The current statement, which is assumed to be a sink - */ - void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt); - - /** - * Records a secondary flow taint reaching a turn-around point. Important: This - * method does not check whether the secondary flow should be recorded. - * - * @param d1 The calling context - * @param source The current taint abstraction - * @param stmt The current statement, which is assumed to be a sink - */ - void processTurnAroundSink(Abstraction d1, Abstraction source, Stmt stmt); -} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java new file mode 100644 index 000000000..a1bf14f8c --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java @@ -0,0 +1,87 @@ +package soot.jimple.infoflow.river; + +import java.util.Collection; +import java.util.Collections; + +import soot.SootMethod; +import soot.jimple.Stmt; +import soot.jimple.infoflow.data.Abstraction; +import soot.jimple.infoflow.data.AbstractionAtSink; +import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; +import soot.jimple.infoflow.util.ByReferenceBoolean; + +public class RiverPropagationRule extends AbstractTaintPropagationRule { + + @Override + public Collection propagateNormalFlow(Abstraction d1, Abstraction source, Stmt stmt, Stmt destStmt, + ByReferenceBoolean killSource, ByReferenceBoolean killAll) { + return null; + } + + @Override + public Collection propagateCallFlow(Abstraction d1, Abstraction source, Stmt stmt, SootMethod dest, + ByReferenceBoolean killAll) { + return null; + } + + @Override + public Collection propagateCallToReturnFlow(Abstraction d1, Abstraction source, Stmt stmt, + ByReferenceBoolean killSource, ByReferenceBoolean killAll) { + return null; + } + + @Override + public Collection propagateReturnFlow(Collection callerD1s, Abstraction calleeD1, + Abstraction source, Stmt stmt, Stmt retSite, Stmt callSite, ByReferenceBoolean killAll) { + return null; + } + + // Note: Do not get confused with on the terms source/sink. In the general case, + // the backward + // analysis starts the analysis at sinks and records results at the source. For + // secondary flows, + // the secondary source is equal to the primary sink and the secondary sink is + // an interesting + // statement (an additional flow condition or a usage context) at which we + // record a result. + // That's why the backward source rule is also the secondary flow sink rule. */ + /** + * Records a secondary flow taint reaching a statement. Important: This method + * does not check whether the secondary flow should be recorded. + * + * @param d1 The calling context + * @param source The current taint abstraction + * @param stmt The current statement, which is assumed to be a sink + */ + public void processSecondaryFlowSink(Abstraction d1, Abstraction source, Stmt stmt) { + // Static fields are not part of the conditional flow model. + if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) + return; + + // Only proceed if stmt could influence the taint + if (!stmt.containsInvokeExpr() || !Utils.isTaintVisibleInCallee(stmt, source, getAliasing())) + return; + + getResults().addResult( + new AbstractionAtSink(Collections.singleton(SecondarySinkDefinition.INSTANCE), source, stmt)); + } + + /** + * Records a secondary flow taint reaching a turn-around point. Important: This + * method does not check whether the secondary flow should be recorded. + * + * @param d1 The calling context + * @param source The current taint abstraction + * @param stmt The current statement, which is assumed to be a sink + */ + public void processTurnAroundSink(Abstraction d1, Abstraction source, Stmt stmt) { + + // Static fields are not part of the conditional flow model. + if (!source.isAbstractionActive() || source.getAccessPath().isStaticFieldRef()) + return; + + getResults().addResult( + new AbstractionAtSink(Collections.singleton(TurnAroundSecondarySinkDefinition.INSTANCE), source, stmt)); + } + +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java index af4409962..b29210cca 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java @@ -16,7 +16,7 @@ * Attach to the backward analysis. */ public class SecondaryFlowListener implements TaintPropagationHandler { - private IAdditionalFlowSinkPropagationRule sinkRule = null; + private RiverPropagationRule sinkRule = null; /** * Ensures that the field sourceRule is always set. @@ -32,8 +32,8 @@ private void ensureSourcePropagationRule(InfoflowManager manager) { PropagationRuleManager ruleManager = manager.getMainSolver().getTabulationProblem().getPropagationRules(); for (ITaintPropagationRule rule : ruleManager.getRules()) { - if (rule instanceof IAdditionalFlowSinkPropagationRule) { - sinkRule = (IAdditionalFlowSinkPropagationRule) rule; + if (rule instanceof RiverPropagationRule) { + sinkRule = (RiverPropagationRule) rule; return; } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java index 54f84ceab..c54c73c3f 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java @@ -17,7 +17,7 @@ * Attach to the backward analysis. */ public class TurnAroundFlowListener implements TaintPropagationHandler { - private IAdditionalFlowSinkPropagationRule sinkRule = null; + private RiverPropagationRule sinkRule = null; /** * Ensures that the field sourceRule is always set. @@ -33,8 +33,8 @@ private void ensureSourcePropagationRule(InfoflowManager manager) { PropagationRuleManager ruleManager = manager.getMainSolver().getTabulationProblem().getPropagationRules(); for (ITaintPropagationRule rule : ruleManager.getRules()) { - if (rule instanceof IAdditionalFlowSinkPropagationRule) { - sinkRule = (IAdditionalFlowSinkPropagationRule) rule; + if (rule instanceof RiverPropagationRule) { + sinkRule = (RiverPropagationRule) rule; return; } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java index 6c266a242..317ab9920 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java @@ -6,11 +6,53 @@ import soot.Unit; import soot.Value; import soot.ValueBox; +import soot.jimple.AssignStmt; +import soot.jimple.InstanceInvokeExpr; +import soot.jimple.InvokeExpr; +import soot.jimple.Stmt; +import soot.jimple.infoflow.aliasing.Aliasing; import soot.jimple.infoflow.data.Abstraction; import soot.jimple.infoflow.data.AccessPath; public class Utils { + /** + * Checks whether the given taint is visible inside the method called at the + * given call site + * + * @param stmt A call site where a sink method is called + * @param source The taint that has arrived at the given statement + * @param aliasing The aliasing strategy + * @return True if the callee has access to the tainted value, false otherwise + */ + public static boolean isTaintVisibleInCallee(Stmt stmt, Abstraction source, Aliasing aliasing) { + InvokeExpr iexpr = stmt.getInvokeExpr(); + + // Is an argument tainted? + final Value apBaseValue = source.getAccessPath().getPlainValue(); + if (apBaseValue != null && aliasing != null) { + for (int i = 0; i < iexpr.getArgCount(); i++) { + if (aliasing.mayAlias(iexpr.getArg(i), apBaseValue)) { + if (source.getAccessPath().getTaintSubFields() || source.getAccessPath().isLocal()) + return true; + } + } + } + + // Is the base object tainted? + if (iexpr instanceof InstanceInvokeExpr) { + if (((InstanceInvokeExpr) iexpr).getBase() == source.getAccessPath().getPlainValue()) + return true; + } + + // Is return tainted? + if (stmt instanceof AssignStmt && aliasing != null + && aliasing.mayAlias(apBaseValue, ((AssignStmt) stmt).getLeftOp())) + return true; + + return false; + } + /** * Check whether baseLocal is tainted in the outgoing set. Assumes baseLocal is * an object and the check happens at a call site. From 09179c5c88f2e594263904362730c4dc89bc46e4 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 30 Mar 2026 10:59:29 +0200 Subject: [PATCH 08/18] JavaDoc --- .../jimple/infoflow/data/ValueOnPath.java | 86 ++++++++++++++++++- .../infoflow/river/RiverPropagationRule.java | 4 + 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java index bda6b0d2f..ce84481b8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java +++ b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java @@ -5,8 +5,16 @@ import java.util.Set; import java.util.regex.Pattern; +/** + * Can be used to express conditions on values that are used in statements on + * the path. This class can be used to enforce that there is a specific method + * invocation call where a specific constant value is used as a parameter. + */ public class ValueOnPath { + /** + * A value condition on a specific parameter + */ public static class Parameter { private int paramIdx; @@ -14,20 +22,44 @@ public static class Parameter { private String value; private Pattern matcher; + /** + * Creates a new parameter + * + * @param paramIndex the 0-based parameter index + * @param regex whether the parameter value is used as a regular + * expression + * @param casesensitive whether the check is case sensitive + */ public Parameter(int paramIndex, boolean regex, boolean casesensitive) { this.paramIdx = paramIndex; this.regex = regex; this.casesensitive = casesensitive; } + /** + * Returns the parameter index where the parameter condition applies + * + * @return parameter index + */ public int getParameterIndex() { return paramIdx; } + /** + * Returns true if the parameter value is treated as a regular expression. If + * false, it is a regular string equals check + * + * @return whether the value is treated as a regular expression + */ public boolean isRegex() { return regex; } + /** + * Returns true if the parameter value is checked case sensitive + * + * @return whether the value is checked case sensitive + */ public boolean isCaseSensitive() { return casesensitive; } @@ -64,25 +96,61 @@ public Pattern getRegexMatcher() { return matcher; } + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Value '" + value + "' on parameter " + paramIdx); + if (regex) + sb.append(", regex"); + if (casesensitive) + sb.append(", case sensitive"); + return sb.toString(); + } + } private String invocation; private Set parameters; + /** + * Creates a new value on path condition + * + * @param inv the invocation site where this condition applies + */ public ValueOnPath(String inv) { this.invocation = inv; } + /** + * Returns the soot method signature of the invocation site where this condition + * applies + * + * @return the invocation site + */ public String getInvocation() { return invocation; } - public void add(Parameter parameter) { + /** + * Adds a parameter condition. Note that the parameters are combined using + * AND, i.e. all of them have to be true in order to fulfill this + * condition. + * + * @param parameter the new parameters condition + * @return true if the parameter condition has been added successfully + */ + public boolean add(Parameter parameter) { if (parameters == null) parameters = new HashSet<>(); - parameters.add(parameter); + return parameters.add(parameter); } + /** + * Returns a set of parameter conditions. Note that the parameters are combined + * using AND, i.e. all of them have to be true in order to fulfill this + * condition. + * + * @return the parameters + */ public Set getParameters() { return parameters; } @@ -104,4 +172,18 @@ public boolean equals(Object obj) { return Objects.equals(invocation, other.invocation) && Objects.equals(parameters, other.parameters); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Value condition on invocation site " + getInvocation() + ": "); + boolean first = true; + for (Parameter p : getParameters()) { + if (first) + first = false; + else + sb.append(", "); + sb.append(p.toString()); + } + return super.toString(); + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java index a1bf14f8c..43387f7f9 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java @@ -10,6 +10,10 @@ import soot.jimple.infoflow.problems.rules.AbstractTaintPropagationRule; import soot.jimple.infoflow.util.ByReferenceBoolean; +/** + * Contains river-specific code that i.e. adds dataflow results when secondary + * sinks are reached. + */ public class RiverPropagationRule extends AbstractTaintPropagationRule { @Override From e9609964e3cb2135be2ee3c6bf262bb3958177c3 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 8 Apr 2026 13:57:45 +0200 Subject: [PATCH 09/18] Add summaries --- ...droid.content.SharedPreferences$Editor.xml | 260 ++++++++++ .../android.content.SharedPreferences.xml | 153 ++++++ .../summariesManual/java.io.OutputStream.xml | 20 + .../summariesManual/java.net.URL.xml | 6 + .../summariesManual/java.sql.Connection.xml | 116 +++++ .../java.sql.PreparedStatement.xml | 459 ++++++++++++++++++ .../summariesManual/java.sql.Statement.xml | 48 ++ 7 files changed, 1062 insertions(+) create mode 100644 soot-infoflow-summaries/summariesManual/android.content.SharedPreferences$Editor.xml create mode 100644 soot-infoflow-summaries/summariesManual/android.content.SharedPreferences.xml create mode 100644 soot-infoflow-summaries/summariesManual/java.sql.Connection.xml create mode 100644 soot-infoflow-summaries/summariesManual/java.sql.PreparedStatement.xml create mode 100644 soot-infoflow-summaries/summariesManual/java.sql.Statement.xml diff --git a/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences$Editor.xml b/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences$Editor.xml new file mode 100644 index 000000000..242e8d2c5 --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences$Editor.xml @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences.xml b/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences.xml new file mode 100644 index 000000000..4576b4afa --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/android.content.SharedPreferences.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/summariesManual/java.io.OutputStream.xml b/soot-infoflow-summaries/summariesManual/java.io.OutputStream.xml index f78005a0f..7be96773a 100644 --- a/soot-infoflow-summaries/summariesManual/java.io.OutputStream.xml +++ b/soot-infoflow-summaries/summariesManual/java.io.OutputStream.xml @@ -13,6 +13,14 @@ + + + + + + @@ -24,6 +32,12 @@ AccessPath="[java.io.OutputStream: java.io.OutputStream innerStream]" AccessPathTypes="[java.io.OutputStream]" /> + + + + @@ -34,6 +48,12 @@ AccessPath="[java.io.OutputStream: java.io.OutputStream innerStream]" AccessPathTypes="[java.io.OutputStream]" /> + + + + diff --git a/soot-infoflow-summaries/summariesManual/java.net.URL.xml b/soot-infoflow-summaries/summariesManual/java.net.URL.xml index 502794c9b..9f103e172 100644 --- a/soot-infoflow-summaries/summariesManual/java.net.URL.xml +++ b/soot-infoflow-summaries/summariesManual/java.net.URL.xml @@ -601,6 +601,12 @@ AccessPath="[java.net.URLConnection: java.net.URL url]" AccessPathTypes="[java.net.URL]" /> + + + + diff --git a/soot-infoflow-summaries/summariesManual/java.sql.Connection.xml b/soot-infoflow-summaries/summariesManual/java.sql.Connection.xml new file mode 100644 index 000000000..fb937e811 --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/java.sql.Connection.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/summariesManual/java.sql.PreparedStatement.xml b/soot-infoflow-summaries/summariesManual/java.sql.PreparedStatement.xml new file mode 100644 index 000000000..e2cda190b --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/java.sql.PreparedStatement.xml @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/summariesManual/java.sql.Statement.xml b/soot-infoflow-summaries/summariesManual/java.sql.Statement.xml new file mode 100644 index 000000000..4db0df9b8 --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/java.sql.Statement.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c7ef7f3338eb4e5579290655ba604d76b0d087a1 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 8 Apr 2026 14:39:50 +0200 Subject: [PATCH 10/18] Log in case of a non-constant found --- .../river/conditions/SignatureFlowCondition.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java index 3cddc1a0d..a00422015 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java @@ -7,6 +7,9 @@ import java.util.Set; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import heros.solver.Pair; import soot.Scene; import soot.SootClass; @@ -42,6 +45,7 @@ * */ public class SignatureFlowCondition extends SourceSinkCondition { + private final Logger logger = LoggerFactory.getLogger(getClass()); private final Set classNamesOnPath; private final Set signaturesOnPath; @@ -360,9 +364,10 @@ else if (v instanceof NumericConstant) cmp = String.valueOf(((NumericConstant) v).getNumericValue()); else if (v instanceof ClassConstant) cmp = ((ClassConstant) v).getValue(); - else + else { + logger.warn(String.format("Unsupported constant type %s: %s", v.getType(), v)); continue nextStmt; - + } String vopContent = p.getContentToMatch(); boolean matched = false; @@ -377,6 +382,11 @@ else if (v instanceof ClassConstant) } if (!matched) continue nextStmt; + } else { + logger.warn(String.format( + "Non-constant used at parameter %d at statement %s in %s, cannot be evaluated", idx, + s.toString(), s.getContainingBody().getMethod().getSignature())); + continue nextStmt; } } // all matched From 110fbcfe0213ccc790e2e735231d4a51a1bb4a66 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 8 Apr 2026 15:34:39 +0200 Subject: [PATCH 11/18] Add new summaries --- .../summariesManual/javax.crypto.Cipher.xml | 24 +++++++++ .../javax.crypto.CipherOutputStream.xml | 51 +++++++++++++++++++ .../manager/BaseSourceSinkManager.java | 5 +- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 soot-infoflow-summaries/summariesManual/javax.crypto.CipherOutputStream.xml diff --git a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml index a262a17b3..42f6c52c1 100644 --- a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml +++ b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml @@ -1,6 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/summariesManual/javax.crypto.CipherOutputStream.xml b/soot-infoflow-summaries/summariesManual/javax.crypto.CipherOutputStream.xml new file mode 100644 index 000000000..d89e098ca --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/javax.crypto.CipherOutputStream.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java index 91a1892cf..f87f8108f 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java @@ -1085,10 +1085,11 @@ public void excludeMethod(SootMethod toExclude) { @Override public boolean isSecondarySink(Stmt stmt) { - if (!stmt.containsInvokeExpr() || !(stmt.getInvokeExpr() instanceof InstanceInvokeExpr)) + InvokeExpr inv = stmt.getInvokeExprUnsafe(); + if (inv == null) return false; - SootMethod callee = stmt.getInvokeExpr().getMethod(); + SootMethod callee = inv.getMethod(); SootClass dc = callee.getDeclaringClass(); if (secondarySinkMethods.contains(callee) || secondarySinkClasses.contains(dc)) return true; From b52730d917447a66f79a90492c61b55e47cc3e3e Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 8 Apr 2026 15:34:54 +0200 Subject: [PATCH 12/18] Correct typo in JavaDoc --- .../soot/jimple/infoflow/problems/AbstractInfoflowProblem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java b/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java index 4917b85af..afcf4f5b8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java +++ b/soot-infoflow/src/soot/jimple/infoflow/problems/AbstractInfoflowProblem.java @@ -46,7 +46,7 @@ /** * abstract super class which - concentrates functionality used by * InfoflowProblem and AliasProblem - contains helper functions which should not - * pollute the naturally large InfofflowProblems + * pollute the naturally large InfoflowProblems * */ public abstract class AbstractInfoflowProblem extends DefaultJimpleIFDSTabulationProblem { From 0a80fc5cb8ebc2ca71f75514a337d4b8224dd322 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Wed, 8 Apr 2026 15:46:36 +0200 Subject: [PATCH 13/18] Add insecure cipher summary --- .../summariesManual/javax.crypto.Cipher.xml | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml index 42f6c52c1..7c5e9f5c1 100644 --- a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml +++ b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml @@ -7,6 +7,10 @@ + + + + @@ -15,6 +19,10 @@ + + + + @@ -23,6 +31,10 @@ + + + + @@ -46,6 +58,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -55,6 +76,11 @@ + + + + @@ -69,6 +95,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -83,6 +118,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -97,6 +137,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -113,6 +158,11 @@ + + + + @@ -128,6 +178,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -142,6 +201,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -156,6 +224,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -170,6 +243,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -186,6 +264,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -200,6 +287,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -216,6 +312,11 @@ + + + + From e0d4703577256ccbf50921eac8e08c93af4e06a3 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Thu, 9 Apr 2026 14:26:28 +0200 Subject: [PATCH 14/18] Support for additional flow triggers on parameters and return values --- .../schema/SourcesAndSinks.xsd | 1 + .../xml/AbstractXMLSourceSinkParser.java | 11 ++++ .../source/parsers/xml/XMLConstants.java | 1 + soot-infoflow-cmd/schema/SourcesAndSinks.xsd | 1 + .../summariesManual/javax.crypto.Cipher.xml | 4 ++ ...nditionalFlowSourceSinkManagerWrapper.java | 5 +- .../river/IConditionalFlowManager.java | 5 +- .../river/SecondaryFlowGenerator.java | 25 ++++++-- .../definitions/AccessPathTuple.java | 11 +++- .../IAdditionalFlowTriggerInformation.java | 20 ++++++ .../MethodSourceSinkDefinition.java | 61 ++++++++++++++++++- .../manager/BaseSourceSinkManager.java | 29 ++++++--- .../manager/ConditionalSinkInfo.java | 47 ++++++++++++++ 13 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/IAdditionalFlowTriggerInformation.java create mode 100644 soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ConditionalSinkInfo.java diff --git a/soot-infoflow-android/schema/SourcesAndSinks.xsd b/soot-infoflow-android/schema/SourcesAndSinks.xsd index c3f217e2f..c32b5eeac 100644 --- a/soot-infoflow-android/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-android/schema/SourcesAndSinks.xsd @@ -60,6 +60,7 @@ + diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java index 874a446b3..99b70bd26 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/AbstractXMLSourceSinkParser.java @@ -107,6 +107,7 @@ protected class SAXHandler extends DefaultHandler { protected String accessPathParentElement = ""; protected String description = ""; + protected boolean triggerAdditionalFlow; protected Set baseAPs = new HashSet<>(); protected List> paramAPs = new ArrayList<>(); @@ -164,11 +165,16 @@ public void startElement(String uri, String localName, String qName, Attributes case XMLConstants.BASE_TAG: accessPathParentElement = qNameLower; description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + String t = attributes.getValue(XMLConstants.TRIGGERADDITIONALFLOW_ATTRIBUTE); + // true by default for the base tag + triggerAdditionalFlow = t == null || t.equalsIgnoreCase("true"); break; case XMLConstants.RETURN_TAG: accessPathParentElement = qNameLower; description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + t = attributes.getValue(XMLConstants.TRIGGERADDITIONALFLOW_ATTRIBUTE); + triggerAdditionalFlow = t != null && t.equalsIgnoreCase("true"); break; case XMLConstants.PARAM_TAG: @@ -295,6 +301,8 @@ protected void handleStarttagParam(Attributes attributes, String qNameLower) { } accessPathParentElement = qNameLower; description = attributes.getValue(XMLConstants.DESCRIPTION_ATTRIBUTE); + String t = attributes.getValue(XMLConstants.TRIGGERADDITIONALFLOW_ATTRIBUTE); + triggerAdditionalFlow = t != null && t.equalsIgnoreCase("true"); } protected void handleStarttagPathelement(Attributes attributes) { @@ -547,6 +555,8 @@ protected void handleEndtagAccesspath() { if (description != null && !description.isEmpty()) apt.setDescription(description); + apt.setTriggerAdditionalFlow(triggerAdditionalFlow); + // Simplify the AP after setting the description for not breaking the generic // source definition apt = apt.simplify(); @@ -573,6 +583,7 @@ protected void handleEndtagAccesspath() { isSink = false; pathElements = null; pathElementTypes = null; + triggerAdditionalFlow = false; description = null; } diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java index 422d1cafb..b7f91c3c7 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java @@ -38,6 +38,7 @@ public class XMLConstants { public static final String DESCRIPTION_ATTRIBUTE = "description"; public static final String REGEX_ATTRIBUTE = "regex"; public static final String CASE_SENSITIVE_ATTRIBUTE = "caseSensitive"; + public static final String TRIGGERADDITIONALFLOW_ATTRIBUTE = "triggerAdditionalFlow"; public static final String TRUE = "true"; diff --git a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd index c3f217e2f..c32b5eeac 100644 --- a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd @@ -60,6 +60,7 @@ + diff --git a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml index 7c5e9f5c1..0f7df165f 100644 --- a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml +++ b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml @@ -44,6 +44,10 @@ AccessPathTypes="[byte[]]" /> + + + + diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java index 63e5be5a8..4e1aefe61 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java @@ -5,6 +5,7 @@ import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.AccessPath; +import soot.jimple.infoflow.sourcesSinks.manager.ConditionalSinkInfo; import soot.jimple.infoflow.sourcesSinks.manager.IReversibleSourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.sourcesSinks.manager.SourceInfo; @@ -39,8 +40,8 @@ public void registerSecondarySink(SootMethod sm) { } @Override - public boolean isConditionalSink(Stmt stmt, SootClass baseClass) { - return inner.isConditionalSink(stmt, baseClass); + public ConditionalSinkInfo getConditionalSinkInfo(Stmt stmt, SootClass baseClass) { + return inner.getConditionalSinkInfo(stmt, baseClass); } @Override diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java index 8d6c3a2ca..6f6df4ed8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java @@ -3,6 +3,7 @@ import soot.SootClass; import soot.SootMethod; import soot.jimple.Stmt; +import soot.jimple.infoflow.sourcesSinks.manager.ConditionalSinkInfo; /** * A source sink manager that is able to manage sinks with conditions, i.e. the @@ -25,9 +26,9 @@ public interface IConditionalFlowManager { * * @param stmt Sink Statement * @param baseClass Class of the tainted base - * @return true if stmt is a conditional sink + * @return information about the conditional sink */ - boolean isConditionalSink(Stmt stmt, SootClass baseClass); + ConditionalSinkInfo getConditionalSinkInfo(Stmt stmt, SootClass baseClass); /** * Register a secondary sink at runtime. diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java index cef8b5820..4bdd5ff60 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java @@ -5,8 +5,10 @@ import java.util.Set; import heros.solver.PathEdge; +import soot.Local; import soot.RefType; import soot.Unit; +import soot.Value; import soot.jimple.InstanceInvokeExpr; import soot.jimple.Stmt; import soot.jimple.infoflow.InfoflowManager; @@ -15,6 +17,7 @@ import soot.jimple.infoflow.handlers.TaintPropagationHandler; import soot.jimple.infoflow.problems.TaintPropagationResults; import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; +import soot.jimple.infoflow.sourcesSinks.manager.ConditionalSinkInfo; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; /** @@ -71,15 +74,27 @@ public boolean notifyFlowOut(Unit unit, Abstraction d1, Abstraction incoming, Se // Check for sink contexts if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof InstanceInvokeExpr) { - Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, - ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase()); + Value baseLocal = ((InstanceInvokeExpr) stmt.getInvokeExpr()).getBase(); + Abstraction baseTaint = Utils.getTaintFromLocal(outgoing, baseLocal); // Is the base tainted in the outgoing set? if (baseTaint != null && baseTaint.getAccessPath().getBaseType() instanceof RefType) { RefType ref = (RefType) baseTaint.getAccessPath().getBaseType(); - if (condFlowManager.isConditionalSink(stmt, ref.getSootClass())) { - Abstraction newAbs = createAdditionalFlowAbstraction(baseTaint, stmt); - additionalAbsSet.add(newAbs); + ConditionalSinkInfo info = condFlowManager.getConditionalSinkInfo(stmt, ref.getSootClass()); + if (info != null) { + Set locals = info.getTriggeredAdditionalFlows(stmt); + if (locals != null) { + for (Local l : locals) { + Abstraction newAbs; + if (l == baseLocal) { + newAbs = createAdditionalFlowAbstraction(baseTaint, stmt); + } else { + newAbs = createAdditionalFlowAbstraction(baseTaint.deriveNewAbstraction( + manager.getAccessPathFactory().createAccessPath(l, true), stmt), stmt); + } + additionalAbsSet.add(newAbs); + } + } } } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AccessPathTuple.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AccessPathTuple.java index 9c4ef866f..c0841d93b 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AccessPathTuple.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/AccessPathTuple.java @@ -14,8 +14,8 @@ import soot.jimple.infoflow.InfoflowManager; import soot.jimple.infoflow.data.AccessPath; import soot.jimple.infoflow.data.AccessPath.ArrayTaintType; -import soot.jimple.infoflow.typing.TypeUtils; import soot.jimple.infoflow.data.AccessPathFragment; +import soot.jimple.infoflow.typing.TypeUtils; /** * Helper to save an AccessPath with the information about sink and sources. @@ -32,6 +32,7 @@ public class AccessPathTuple { private String description; private int hashCode = 0; + private boolean triggerAdditionalFlow; private static AccessPathTuple SOURCE_TUPLE; private static AccessPathTuple SINK_TUPLE; @@ -307,4 +308,12 @@ public String toString() { return sb.toString(); } + public void setTriggerAdditionalFlow(boolean triggerAdditionalFlow) { + this.triggerAdditionalFlow = triggerAdditionalFlow; + } + + public boolean isTriggerAdditionalFlow() { + return triggerAdditionalFlow; + } + } diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/IAdditionalFlowTriggerInformation.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/IAdditionalFlowTriggerInformation.java new file mode 100644 index 000000000..9acef7bd7 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/IAdditionalFlowTriggerInformation.java @@ -0,0 +1,20 @@ +package soot.jimple.infoflow.sourcesSinks.definitions; + +import java.util.Set; + +import soot.Local; +import soot.jimple.Stmt; + +/** + * Contains information about for what local variables to trigger additional + * flows + */ +public interface IAdditionalFlowTriggerInformation { + /** + * Returns a set of local variables for which to trigger additional flows + * + * @param stmt the statement + * @return the set of local variables + */ + public Set getTriggeredAdditionalFlows(Stmt stmt); +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java index 9b19a89d0..8e984ecd5 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/definitions/MethodSourceSinkDefinition.java @@ -6,6 +6,12 @@ import java.util.HashSet; import java.util.Set; +import soot.Local; +import soot.Value; +import soot.jimple.AssignStmt; +import soot.jimple.InstanceInvokeExpr; +import soot.jimple.InvokeExpr; +import soot.jimple.Stmt; import soot.jimple.infoflow.data.SootMethodAndClass; /** @@ -16,7 +22,7 @@ * */ public class MethodSourceSinkDefinition extends AbstractSourceSinkDefinition - implements IAccessPathBasedSourceSinkDefinition { + implements IAccessPathBasedSourceSinkDefinition, IAdditionalFlowTriggerInformation { private static MethodSourceSinkDefinition BASE_OBJ_SOURCE; private static MethodSourceSinkDefinition BASE_OBJ_SINK; @@ -513,6 +519,59 @@ public Set getAllAccessPaths() { return aps; } + @Override + public Set getTriggeredAdditionalFlows(Stmt stmt) { + Set triggered = null; + InvokeExpr inv = stmt.getInvokeExpr(); + if (inv instanceof InstanceInvokeExpr) { + boolean triggeredBase = true; // true by default + if (baseObjects != null) { + triggeredBase = false; + for (AccessPathTuple a : baseObjects) { + if (a.isTriggerAdditionalFlow()) { + triggeredBase = true; + break; + } + } + } + + if (triggeredBase) { + triggered = new HashSet<>(); + Value base = ((InstanceInvokeExpr) inv).getBase(); + triggered.add((Local) base); + } + } + if (parameters != null) { + for (int i = 0; i < parameters.length; i++) { + Value param = inv.getArg(i); + if (parameters[i] != null && param instanceof Local) { + for (AccessPathTuple a : parameters[i]) { + if (a.isTriggerAdditionalFlow()) { + if (triggered == null) + triggered = new HashSet<>(); + triggered.add((Local) param); + break; + } + } + } + } + } + if (returnValues != null && stmt instanceof AssignStmt) { + AssignStmt assign = (AssignStmt) stmt; + + for (AccessPathTuple a : returnValues) { + if (a.isTriggerAdditionalFlow()) { + if (triggered == null) + triggered = new HashSet<>(); + Value ret = assign.getLeftOp(); + triggered.add((Local) ret); + break; + } + } + } + return triggered; + } + @SuppressWarnings("unchecked") @Override public MethodSourceSinkDefinition filter(Collection accessPaths) { diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java index f87f8108f..6c609079d 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/BaseSourceSinkManager.java @@ -69,6 +69,7 @@ public abstract class BaseSourceSinkManager implements IReversibleSourceSinkManager, IOneSourceAtATimeManager, IConditionalFlowManager { + private final static String GLOBAL_SIG = "--GLOBAL--"; private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -110,7 +111,7 @@ public static enum SourceType { protected MultiMap sinkFields; protected MultiMap sinkStatements; - protected Set conditionalSinks = new HashSet<>(); + protected Map conditionalSinks = new HashMap<>(); protected MultiMap conditionalSinkToExcludedClasses = new HashMultiMap<>(); protected Set secondarySinkMethods = new HashSet<>(); protected Set secondarySinkClasses = new HashSet<>(); @@ -842,7 +843,13 @@ private void initializeConditions(SootMethod m, ISourceSinkDefinition def) { if (m == null || def.getConditions() == null || def.getConditions().isEmpty()) return; - conditionalSinks.add(m); + conditionalSinks.compute(m, (key, existing) -> { + if (existing == null) + existing = new ConditionalSinkInfo(def); + else + existing.add(def); + return existing; + }); for (SourceSinkCondition cond : def.getConditions()) { conditionalSinkToExcludedClasses.putAll(m, cond.getExcludedClasses()); secondarySinkMethods.addAll(cond.getReferencedMethods()); @@ -1138,24 +1145,28 @@ private boolean isExcludedInCondition(SootMethod matchedSink, SootClass baseClas } @Override - public boolean isConditionalSink(Stmt stmt, SootClass baseClass) { + public ConditionalSinkInfo getConditionalSinkInfo(Stmt stmt, SootClass baseClass) { // River only supports InstanceInvokeExprs if (!stmt.containsInvokeExpr() || !(stmt.getInvokeExpr() instanceof InstanceInvokeExpr)) - return false; + return null; // Check if we have a direct hit SootMethod callee = stmt.getInvokeExpr().getMethod(); - if (conditionalSinks.contains(callee)) - return !isExcludedInCondition(callee, baseClass); + ConditionalSinkInfo info = conditionalSinks.get(callee); + if (info != null && !isExcludedInCondition(callee, baseClass)) { + return info; + } // Check if the current method inherits from a conditional sink for (SootClass sc : parentClassesAndInterfaces.getUnchecked(callee.getDeclaringClass())) { SootMethod superMethod = sc.getMethodUnsafe(callee.getSubSignature()); - if (conditionalSinks.contains(superMethod)) - return !isExcludedInCondition(superMethod, baseClass); + info = conditionalSinks.get(superMethod); + if (info != null && !isExcludedInCondition(superMethod, baseClass)) { + return info; + } } - return false; + return null; } @Override diff --git a/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ConditionalSinkInfo.java b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ConditionalSinkInfo.java new file mode 100644 index 000000000..43ba7e7e7 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/sourcesSinks/manager/ConditionalSinkInfo.java @@ -0,0 +1,47 @@ +package soot.jimple.infoflow.sourcesSinks.manager; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import soot.Local; +import soot.jimple.Stmt; +import soot.jimple.infoflow.sourcesSinks.definitions.IAdditionalFlowTriggerInformation; +import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition; + +public final class ConditionalSinkInfo implements IAdditionalFlowTriggerInformation { + private List triggerInformation = new ArrayList<>(); + + public ConditionalSinkInfo(ISourceSinkDefinition def) { + add(def); + } + + public void add(ISourceSinkDefinition def) { + if (def instanceof IAdditionalFlowTriggerInformation) { + triggerInformation.add((IAdditionalFlowTriggerInformation) def); + } + } + + @Override + public Set getTriggeredAdditionalFlows(Stmt stmt) { + Set l = null; + boolean createdSet = false; + for (IAdditionalFlowTriggerInformation t : triggerInformation) { + Set tr = t.getTriggeredAdditionalFlows(stmt); + if (tr != null && !tr.isEmpty()) { + if (l == null) + l = tr; + else { + if (!createdSet) { + l = new HashSet<>(l); + createdSet = true; + } + l.addAll(tr); + } + } + } + return l; + } + +} \ No newline at end of file From 0f6fe47a38bedaf058f93b4cbaeda5ee816df3b7 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Thu, 9 Apr 2026 21:04:34 +0200 Subject: [PATCH 15/18] Add support for stereotypes --- .../schema/SourcesAndSinks.xsd | 26 +- .../parsers/xml/PreprocessorHandler.java | 265 ++++++++++++++++++ .../parsers/xml/XMLSourceSinkParser.java | 2 +- soot-infoflow-cmd/schema/SourcesAndSinks.xsd | 26 +- .../methodSummary/xml/SummaryReader.java | 10 +- .../java.security.MessageDigest.xml | 195 +++++++++++++ .../jimple/infoflow/data/ValueOnPath.java | 11 +- .../xml/InfoflowResultsSerializer.java | 5 +- .../conditions/SignatureFlowCondition.java | 18 +- 9 files changed, 532 insertions(+), 26 deletions(-) create mode 100644 soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/PreprocessorHandler.java create mode 100644 soot-infoflow-summaries/summariesManual/java.security.MessageDigest.xml diff --git a/soot-infoflow-android/schema/SourcesAndSinks.xsd b/soot-infoflow-android/schema/SourcesAndSinks.xsd index c32b5eeac..423027874 100644 --- a/soot-infoflow-android/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-android/schema/SourcesAndSinks.xsd @@ -3,18 +3,34 @@ - + - + + + + + + + + + + + + + + + + + @@ -27,6 +43,7 @@ + @@ -41,6 +58,7 @@ + @@ -57,6 +75,7 @@ + @@ -82,6 +101,7 @@ + @@ -112,6 +132,7 @@ + @@ -135,6 +156,7 @@ + diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/PreprocessorHandler.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/PreprocessorHandler.java new file mode 100644 index 000000000..0b962563a --- /dev/null +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/PreprocessorHandler.java @@ -0,0 +1,265 @@ +package soot.jimple.infoflow.android.source.parsers.xml; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.ext.Attributes2Impl; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Resolves stereotypes, which are essentially like C macros. + */ +public class PreprocessorHandler extends DefaultHandler { + /* Example: + + + .*${ALGORITHMNAME}.* + + + .*${ALGORITHMNAME}.* + + + .*${ALGORITHMNAME}.* + + + .*${ALGORITHMNAME}.* + + + .*${ALGORITHMNAME}.* + + + .*${ALGORITHMNAME}.* + + + + + + DES + + + RC4 + + + Blowfish + + + */ + + static interface IHandler { + public void apply(DefaultHandler handler, Map replacements) throws SAXException; + } + + private static class CharacterHandler implements IHandler { + + private String string; + + public CharacterHandler(char[] ch, int start, int length) { + this.string = new String(ch, start, length); + } + + @Override + public void apply(DefaultHandler handler, Map replacements) throws SAXException { + String s = string; + for (Entry i : replacements.entrySet()) { + s = s.replace(i.getKey(), i.getValue()); + } + handler.characters(s.toCharArray(), 0, s.length()); + } + + } + + private static class StartElementHandler implements IHandler { + + private String uri; + private String localName; + private String qName; + private Attributes attributes; + + public StartElementHandler(String uri, String localName, String qName, Attributes attributes) { + this.uri = uri; + this.localName = localName; + this.qName = qName; + //we have to clone + this.attributes = new Attributes2Impl(attributes); + } + + @Override + public void apply(DefaultHandler handler, Map replacements) throws SAXException { + Attributes2Impl copy = new Attributes2Impl(); + for (int i = 0; i < attributes.getLength(); i++) { + String uri = attributes.getURI(i); + String localName = attributes.getLocalName(i); + String qName = attributes.getQName(i); + String type = attributes.getType(i); + String value = attributes.getValue(i); + if (value != null) { + for (Entry e : replacements.entrySet()) { + value = value.replace(e.getKey(), e.getValue()); + } + } + copy.addAttribute(uri, localName, qName, type, value); + } + handler.startElement(uri, localName, qName, copy); + } + + } + + private static class EndElementHandler implements IHandler { + + private String uri; + private String localName; + private String qName; + + public EndElementHandler(String uri, String localName, String qName) { + this.uri = uri; + this.localName = localName; + this.qName = qName; + } + + @Override + public void apply(DefaultHandler handler, Map replacements) throws SAXException { + handler.endElement(uri, localName, qName); + } + + } + + private static class RecordingHandler extends DefaultHandler implements IHandler { + private List children = new ArrayList<>(); + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + children.add(new CharacterHandler(ch, start, length)); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) + throws SAXException { + children.add(new StartElementHandler(uri, localName, qName, attributes)); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + children.add(new EndElementHandler(uri, localName, qName)); + } + + public void apply(DefaultHandler handler, Map replacements) throws SAXException { + for (IHandler c : children) + c.apply(handler, replacements); + + } + + } + + private DefaultHandler inner; + private Map stereotypes = new HashMap<>(); + private RecordingHandler definingStereotype; + private String stereotypeId; + + private Map replacements; + private String replacementVariable; + private String replacementValue; + + public PreprocessorHandler(DefaultHandler inner) { + this.inner = inner; + } + + @Override + public void startDocument() throws SAXException { + inner.startDocument(); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (definingStereotype != null) { + definingStereotype.characters(ch, start, length); + } else if (replacementVariable != null) { + this.replacementValue = new String(ch, start, length); + } else { + inner.characters(ch, start, length); + } + + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (definingStereotype != null) { + if (qName.equals("defineStereotype")) + throw new IllegalArgumentException("Defining a stereotype within a stereotype is not supported"); + definingStereotype.startElement(uri, localName, qName, attributes); + return; + } + switch (qName) { + case "defineStereotype": + if (stereotypeId != null) + throw new IllegalStateException(); + stereotypeId = attributes.getValue("id"); + if (stereotypeId == null) + throw new IllegalArgumentException("No stereotype id defined"); + definingStereotype = new RecordingHandler(); + return; + case "stereotype": + if (stereotypeId != null) + throw new IllegalStateException(); + stereotypeId = attributes.getValue("id"); + replacements = new HashMap<>(); + if (stereotypeId == null) + throw new IllegalArgumentException("No stereotype id defined"); + return; + default: + if (replacements != null) { + //within stereotype replacement + this.replacementVariable = qName; + return; + } + } + inner.startElement(uri, localName, qName, attributes); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (definingStereotype != null && !qName.equals("defineStereotype")) { + definingStereotype.endElement(uri, localName, qName); + return; + } + switch (qName) { + case "defineStereotype": + stereotypes.putIfAbsent(stereotypeId, definingStereotype); + definingStereotype = null; + stereotypeId = null; + return; + case "stereotype": + RecordingHandler st = stereotypes.get(stereotypeId); + if (st == null) + throw new IllegalArgumentException( + String.format("Stereotype %s was not found, known stereotypes at this point: %s", stereotypeId, + stereotypes.keySet())); + PreprocessorHandler recursion = new PreprocessorHandler(this.inner); + recursion.stereotypes = stereotypes; + st.apply(recursion, replacements); + stereotypeId = null; + replacements = null; + return; + default: + if (replacementVariable != null) { + if (replacementValue == null) + throw new IllegalStateException("No replacement value defined"); + replacements.put("${" + replacementVariable + "}", replacementValue); + replacementValue = null; + replacementVariable = null; + return; + } + } + inner.endElement(uri, localName, qName); + } + + @Override + public void endDocument() throws SAXException { + inner.endDocument(); + } + +} diff --git a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java index d92fc2df0..b00d852c1 100644 --- a/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java +++ b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLSourceSinkParser.java @@ -257,7 +257,7 @@ protected IAccessPathBasedSourceSinkDefinition createFieldSourceSinkDefinition(S @Override protected void runParse(SAXParser parser, InputStream stream) { try { - parser.parse(stream, new SAXHandler(this.categoryFilter)); + parser.parse(stream, new PreprocessorHandler(new SAXHandler(this.categoryFilter))); } catch (SAXException | IOException e) { e.printStackTrace(); } diff --git a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd index c32b5eeac..423027874 100644 --- a/soot-infoflow-cmd/schema/SourcesAndSinks.xsd +++ b/soot-infoflow-cmd/schema/SourcesAndSinks.xsd @@ -3,18 +3,34 @@ - + - + + + + + + + + + + + + + + + + + @@ -27,6 +43,7 @@ + @@ -41,6 +58,7 @@ + @@ -57,6 +75,7 @@ + @@ -82,6 +101,7 @@ + @@ -112,6 +132,7 @@ + @@ -135,6 +156,7 @@ + diff --git a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java index a62cb3ceb..4d9cc5d72 100644 --- a/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java +++ b/soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/xml/SummaryReader.java @@ -82,8 +82,8 @@ public boolean isValidForThisThread() { private CachedFactory cachedFactory; /** - * Reads a summary xml and places the new summaries into the given data object. - * This method closes the reader. + * Reads a summary xml and places the new summaries into the given data object. This + * method closes the reader. * * @param reader The reader from which to read the method summaries * @param summaries The data object in which to place the summaries @@ -614,6 +614,8 @@ private boolean isMatchStrict(Map attributes) { } private boolean isParameter(Map attributes) { + if (attributes.get(ATTRIBUTE_FLOWTYPE) == null) + System.out.println(); return attributes.get(ATTRIBUTE_FLOWTYPE).equals(SourceSinkType.Parameter.toString()); } @@ -661,8 +663,8 @@ private GapDefinition getGapDefinition(Map attributes, MethodSum /** * Sets whether summaries shall be validated after they are read from disk * - * @param validateSummariesOnRead True if summaries shall be validated after - * they are read from disk, otherwise false + * @param validateSummariesOnRead True if summaries shall be validated after they + * are read from disk, otherwise false */ public void setValidateSummariesOnRead(boolean validateSummariesOnRead) { this.validateSummariesOnRead = validateSummariesOnRead; diff --git a/soot-infoflow-summaries/summariesManual/java.security.MessageDigest.xml b/soot-infoflow-summaries/summariesManual/java.security.MessageDigest.xml new file mode 100644 index 000000000..88f673125 --- /dev/null +++ b/soot-infoflow-summaries/summariesManual/java.security.MessageDigest.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java index ce84481b8..623df96d6 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java +++ b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java @@ -6,9 +6,9 @@ import java.util.regex.Pattern; /** - * Can be used to express conditions on values that are used in statements on - * the path. This class can be used to enforce that there is a specific method - * invocation call where a specific constant value is used as a parameter. + * Can be used to express conditions on values that are used in statements on the path. + * This class can be used to enforce that there is a specific method invocation call + * where a specific constant value is used as a parameter. */ public class ValueOnPath { @@ -132,8 +132,7 @@ public String getInvocation() { /** * Adds a parameter condition. Note that the parameters are combined using - * AND, i.e. all of them have to be true in order to fulfill this - * condition. + * AND, i.e. all of them have to be true in order to fulfill this condition. * * @param parameter the new parameters condition * @return true if the parameter condition has been added successfully @@ -183,7 +182,7 @@ public String toString() { sb.append(", "); sb.append(p.toString()); } - return super.toString(); + return sb.toString(); } } diff --git a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java index cfdb87154..2b4f58b8c 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java +++ b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java @@ -1,5 +1,6 @@ package soot.jimple.infoflow.results.xml; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -68,7 +69,9 @@ public InfoflowResultsSerializer(IInfoflowCFG cfg, InfoflowConfiguration config) */ public void serialize(InfoflowResults results, String fileName) throws XMLStreamException, IOException { this.startTime = System.currentTimeMillis(); - try (OutputStream out = new FileOutputStream(fileName)) { + File f = new File(fileName); + f.getParentFile().mkdirs(); // create parent directories + try (OutputStream out = new FileOutputStream(f)) { XMLOutputFactory factory = XMLOutputFactory.newInstance(); XMLStreamWriter writer = factory.createXMLStreamWriter(out, "UTF-8"); diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java index a00422015..b1d56c4ce 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java @@ -38,8 +38,8 @@ import soot.util.MultiMap; /** - * A condition that checks additional data flow to see whether a source or sink - * is valid or not based on classes and methods on the secondary data flow + * A condition that checks additional data flow to see whether a source or sink is valid + * or not based on classes and methods on the secondary data flow * * @author Steven Arzt * @@ -62,9 +62,9 @@ public class SignatureFlowCondition extends SourceSinkCondition { * @param classNamesOnPath class names that have to be on the path * @param signaturesOnPath signatures that have to be on the path * @param valuesOnPath values that have to be on path - * @param excludedClassNames class names of primary sinks that should be - * filtered without context, e.g. - * ByteArrayOutputStream for OutputStream + * @param excludedClassNames class names of primary sinks that should be filtered + * without context, e.g. ByteArrayOutputStream for + * OutputStream */ public SignatureFlowCondition(Set classNamesOnPath, Set signaturesOnPath, Set valuesOnPath, Set excludedClassNames) { @@ -199,8 +199,7 @@ public Set getExcludedClassNames() { } /** - * Ensures that the set of Soot methods on the data flow path has been - * initialized + * Ensures that the set of Soot methods on the data flow path has been initialized */ private void ensureSootMethodsOnPath() { if (methodsOnPath == null) { @@ -216,8 +215,7 @@ private void ensureSootMethodsOnPath() { } /** - * Ensures that the set of Soot classeson the data flow path has been - * initialized + * Ensures that the set of Soot classeson the data flow path has been initialized */ private void ensureSootClassesOnPath() { if (classesOnPath == null) @@ -445,7 +443,7 @@ public boolean equals(Object obj) { @Override public String toString() { return "AdditionalFlowCondition: " + "classNamesOnPath=" + classNamesOnPath + ", signaturesOnPath=" - + signaturesOnPath + ", excludedClasses=" + excludedClassNames; + + signaturesOnPath + ", excludedClasses=" + excludedClassNames + ", valuesOnPath=" + valuesOnPath; } @Override From b461c360dd378a20d2b43a4b7477d92c90d87b83 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Fri, 17 Apr 2026 16:30:26 +0200 Subject: [PATCH 16/18] Fix URLConnection summary --- .../summariesManual/java.net.URLConnection.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/soot-infoflow-summaries/summariesManual/java.net.URLConnection.xml b/soot-infoflow-summaries/summariesManual/java.net.URLConnection.xml index cdede943c..33666ab07 100644 --- a/soot-infoflow-summaries/summariesManual/java.net.URLConnection.xml +++ b/soot-infoflow-summaries/summariesManual/java.net.URLConnection.xml @@ -217,6 +217,12 @@ AccessPath="[java.io.OutputStream: byte[] innerArray]" AccessPathTypes="[byte[]]" /> + + + + From 7b285612ac0de132935967848f3b08988f564283 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 20 Apr 2026 18:33:45 +0200 Subject: [PATCH 17/18] NPE fix --- .../xml/InfoflowResultsSerializer.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java index 2b4f58b8c..0c4c29ec4 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java +++ b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java @@ -78,14 +78,16 @@ public void serialize(InfoflowResults results, String fileName) throws XMLStream writer.writeStartDocument("UTF-8", "1.0"); writer.writeStartElement(XmlConstants.Tags.root); writer.writeAttribute(XmlConstants.Attributes.fileFormatVersion, FILE_FORMAT_VERSION + ""); - writer.writeAttribute(XmlConstants.Attributes.terminationState, - terminationStateToString(results.getTerminationState())); // Write out the data flow results - if (results != null && !results.isEmpty()) { - writer.writeStartElement(XmlConstants.Tags.results); - writeDataFlows(results, writer); - writer.writeEndElement(); + if (results != null) { + writer.writeAttribute(XmlConstants.Attributes.terminationState, + terminationStateToString(results.getTerminationState())); + if (!results.isEmpty()) { + writer.writeStartElement(XmlConstants.Tags.results); + writeDataFlows(results, writer); + writer.writeEndElement(); + } } // Write out performance data @@ -102,8 +104,7 @@ public void serialize(InfoflowResults results, String fileName) throws XMLStream } /** - * Converts the termination state from the enumeration to a human-readable - * string + * Converts the termination state from the enumeration to a human-readable string * * @param terminationState The termination state * @return A human-readable version of the termination state @@ -126,8 +127,7 @@ private String terminationStateToString(int terminationState) { } /** - * Writes out the given data flow performance data into the given XML stream - * writer + * Writes out the given data flow performance data into the given XML stream writer * * @param performanceData The performance data to write out * @param writer The stream writer into which to write the data @@ -151,8 +151,8 @@ private void writePerformanceData(InfoflowPerformanceData performanceData, XMLSt } /** - * Writes a single performance data entry into the XML file. An entry has a name - * and a value, where the name describes the performance metric. + * Writes a single performance data entry into the XML file. An entry has a name and + * a value, where the name describes the performance metric. * * @param entryName The name that describes the performance metric * @param entryValue The value of the performance metric @@ -342,8 +342,8 @@ protected void writeAccessPath(AccessPath accessPath, XMLStreamWriter writer) th * Sets whether the taint propagation path shall be serialized along with the * respective data flow result * - * @param serialize True if taint propagation paths shall be serialized, - * otherwise false + * @param serialize True if taint propagation paths shall be serialized, otherwise + * false */ public void setSerializeTaintPath(boolean serialize) { this.serializeTaintPath = serialize; From 526ba7cbde87f5301898978190f4d25105a4e5c6 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 20 Apr 2026 18:35:10 +0200 Subject: [PATCH 18/18] NPE fix --- .../results/xml/InfoflowResultsSerializer.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java index 0c4c29ec4..f8c83a3fd 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java +++ b/soot-infoflow/src/soot/jimple/infoflow/results/xml/InfoflowResultsSerializer.java @@ -88,14 +88,14 @@ public void serialize(InfoflowResults results, String fileName) throws XMLStream writeDataFlows(results, writer); writer.writeEndElement(); } - } - // Write out performance data - InfoflowPerformanceData performanceData = results.getPerformanceData(); - if (performanceData != null && !performanceData.isEmpty()) { - writer.writeStartElement(XmlConstants.Tags.performanceData); - writePerformanceData(performanceData, writer); - writer.writeEndElement(); + // Write out performance data + InfoflowPerformanceData performanceData = results.getPerformanceData(); + if (performanceData != null && !performanceData.isEmpty()) { + writer.writeStartElement(XmlConstants.Tags.performanceData); + writePerformanceData(performanceData, writer); + writer.writeEndElement(); + } } writer.writeEndDocument();