diff --git a/soot-infoflow-android/schema/SourcesAndSinks.xsd b/soot-infoflow-android/schema/SourcesAndSinks.xsd index b71d54ae2..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,9 +75,11 @@ + + @@ -81,6 +101,7 @@ + @@ -96,6 +117,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -109,6 +154,9 @@ + + + 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..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 @@ -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; @@ -105,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<>(); @@ -112,11 +115,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() { } @@ -158,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: @@ -177,10 +189,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 +274,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()) @@ -265,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) { @@ -290,6 +328,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 +388,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 +452,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 +486,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 +496,7 @@ protected void handleEndtagMethod() { } // Start a new method and discard our old data + turnAroundPaths = null; methodSignature = null; fieldSignature = null; baseAPs = new HashSet<>(); @@ -489,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(); @@ -515,6 +583,7 @@ protected void handleEndtagAccesspath() { isSink = false; pathElements = null; pathElementTypes = null; + triggerAdditionalFlow = false; description = null; } @@ -666,23 +735,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/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/XMLConstants.java b/soot-infoflow-android/src/soot/jimple/infoflow/android/source/parsers/xml/XMLConstants.java index b85af5c66..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 @@ -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,9 @@ 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 TRIGGERADDITIONALFLOW_ATTRIBUTE = "triggerAdditionalFlow"; 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..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 @@ -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; } @@ -253,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 6e7719332..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 @@ + @@ -50,15 +68,18 @@ + + + @@ -80,6 +101,7 @@ + @@ -95,6 +117,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -108,9 +154,23 @@ + + + + + + + + + + + + + + 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..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 @@ -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; @@ -57,7 +58,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; @@ -81,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 @@ -93,7 +94,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(); @@ -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) { @@ -595,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()); } @@ -642,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/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/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-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.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[]]" /> + + + + 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-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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml index a262a17b3..0f7df165f 100644 --- a/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml +++ b/soot-infoflow-summaries/summariesManual/javax.crypto.Cipher.xml @@ -1,6 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -8,6 +44,10 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -22,6 +62,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -31,6 +80,11 @@ + + + + @@ -45,6 +99,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -59,6 +122,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -73,6 +141,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -89,6 +162,11 @@ + + + + @@ -104,6 +182,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -118,6 +205,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -132,6 +228,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -146,6 +247,11 @@ AccessPathTypes="[byte[]]" /> + + + + @@ -162,6 +268,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -176,6 +291,15 @@ AccessPathTypes="[byte[]]" /> + + + + + + + + @@ -192,6 +316,11 @@ + + + + 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-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/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..2ad1b595a 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java +++ b/soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java @@ -126,6 +126,8 @@ 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.river.TurnAroundFlowListener; import soot.jimple.infoflow.solver.DefaultSolverPeerGroup; import soot.jimple.infoflow.solver.IInfoflowSolver; import soot.jimple.infoflow.solver.ISolverPeerGroup; @@ -667,11 +669,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 +969,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 +992,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,18 +1137,10 @@ public Thread newThread(Runnable r) { if (config.getAdditionalFlowsEnabled()) { // Add the SecondaryFlowGenerator to the main forward taint analysis TaintPropagationHandler forwardHandler = forwardProblem.getTaintPropagationHandler(); - 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()); + forwardHandler = SequentialTaintPropagationHandler.concat(forwardHandler, new SecondaryFlowListener()); + + forwardProblem.setTaintPropagationHandler(forwardHandler); if (!(manager.getSourceSinkManager() instanceof IConditionalFlowManager)) throw new IllegalStateException("Additional Flows enabled but no ConditionalFlowManager in place!"); @@ -1165,7 +1162,11 @@ 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)); + seqTpg.addHandler(new TurnAroundFlowListener()); + additionalProblem.setTaintPropagationHandler(seqTpg); additionalProblem.setTaintWrapper(taintWrapper); additionalNativeCallHandler = new BackwardNativeCallHandler(); additionalProblem.setNativeCallHandler(additionalNativeCallHandler); @@ -1891,6 +1892,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..623df96d6 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/data/ValueOnPath.java @@ -0,0 +1,188 @@ +package soot.jimple.infoflow.data; + +import java.util.HashSet; +import java.util.Objects; +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; + private boolean regex, casesensitive; + 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; + } + + @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; + } + + @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; + } + + /** + * 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<>(); + 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; + } + + @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); + } + + @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 sb.toString(); + } + +} diff --git a/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java b/soot-infoflow/src/soot/jimple/infoflow/handlers/SequentialTaintPropagationHandler.java index f64cbd586..c271d9f49 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; @@ -87,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/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..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 { @@ -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/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 21a0e13f3..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,8 +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.Utils; import soot.jimple.infoflow.sourcesSinks.manager.IReversibleSourceSinkManager; import soot.jimple.infoflow.sourcesSinks.manager.SinkInfo; import soot.jimple.infoflow.util.BaseSelector; @@ -34,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; @@ -105,51 +100,10 @@ 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) { - if (stmt.toString().equals( - "r1 = virtualinvoke r0.()")) - System.out.println("x"); - if (!(manager.getSourceSinkManager() instanceof IReversibleSourceSinkManager)) return null; final IReversibleSourceSinkManager ssm = (IReversibleSourceSinkManager) manager.getSourceSinkManager(); @@ -158,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()); @@ -190,26 +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)); - } } 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..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 @@ -185,7 +185,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) { 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..f8c83a3fd 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,29 +69,33 @@ 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"); 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(); - } - - // Write out performance data - InfoflowPerformanceData performanceData = results.getPerformanceData(); - if (performanceData != null && !performanceData.isEmpty()) { - writer.writeStartElement(XmlConstants.Tags.performanceData); - writePerformanceData(performanceData, 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 + InfoflowPerformanceData performanceData = results.getPerformanceData(); + if (performanceData != null && !performanceData.isEmpty()) { + writer.writeStartElement(XmlConstants.Tags.performanceData); + writePerformanceData(performanceData, writer); + writer.writeEndElement(); + } } writer.writeEndDocument(); @@ -99,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 @@ -123,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 @@ -148,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 @@ -339,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; 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/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..4e1aefe61 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/ConditionalFlowSourceSinkManagerWrapper.java @@ -5,64 +5,72 @@ 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.ConditionalSinkInfo; +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 ConditionalSinkInfo getConditionalSinkInfo(Stmt stmt, SootClass baseClass) { + return inner.getConditionalSinkInfo(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/IAdditionalFlowSinkPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java deleted file mode 100644 index 61bbd6105..000000000 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IAdditionalFlowSinkPropagationRule.java +++ /dev/null @@ -1,23 +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); -} diff --git a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java index e110bd6e0..6f6df4ed8 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/IConditionalFlowManager.java @@ -3,43 +3,55 @@ 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 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 information about the conditional sink + */ + ConditionalSinkInfo getConditionalSinkInfo(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/RiverPropagationRule.java b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java new file mode 100644 index 000000000..43387f7f9 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/RiverPropagationRule.java @@ -0,0 +1,91 @@ +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; + +/** + * Contains river-specific code that i.e. adds dataflow results when secondary + * sinks are reached. + */ +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/SecondaryFlowGenerator.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java index aae6fdd0a..4bdd5ff60 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowGenerator.java @@ -5,17 +5,19 @@ import java.util.Set; import heros.solver.PathEdge; +import soot.Local; 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.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.ConditionalSinkInfo; import soot.jimple.infoflow.sourcesSinks.manager.ISourceSinkManager; /** @@ -56,13 +58,13 @@ 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; // Check whether any use matches the incoming taint - if (!isReadAt(unit, incoming.getAccessPath())) + if (!Utils.isReadAt(unit, incoming.getAccessPath())) return false; ensureCondFlowManager(manager); @@ -72,14 +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 = 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); + } + } } } } @@ -92,6 +107,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 +116,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 +131,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 */ @@ -130,34 +146,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/SecondaryFlowListener.java b/soot-infoflow/src/soot/jimple/infoflow/river/SecondaryFlowListener.java index 33d08f26d..b29210cca 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; @@ -15,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. @@ -31,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; } } @@ -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..2bc2698ae --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowGenerator.java @@ -0,0 +1,108 @@ +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.jimple.InstanceInvokeExpr; +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.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 (!Utils.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 = Utils.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)); + + } + } + } + + 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); + } +} 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..c54c73c3f --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/TurnAroundFlowListener.java @@ -0,0 +1,75 @@ +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 RiverPropagationRule 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 RiverPropagationRule) { + sinkRule = (RiverPropagationRule) 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; + if (!Utils.isReadAt(unit, incoming.getAccessPath())) + 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; + } + +} 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/Utils.java b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java new file mode 100644 index 000000000..317ab9920 --- /dev/null +++ b/soot-infoflow/src/soot/jimple/infoflow/river/Utils.java @@ -0,0 +1,88 @@ +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.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. + * + * @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; + } + +} 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..b1d56c4ce 100644 --- a/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java +++ b/soot-infoflow/src/soot/jimple/infoflow/river/conditions/SignatureFlowCondition.java @@ -1,34 +1,51 @@ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import heros.solver.Pair; import soot.Scene; 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; /** - * 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 * */ public class SignatureFlowCondition extends SourceSinkCondition { + private final Logger logger = LoggerFactory.getLogger(getClass()); private final Set classNamesOnPath; private final Set signaturesOnPath; @@ -37,25 +54,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 excludedClassNames class names of primary sinks that should be - * filtered without context, e.g. - * ByteArrayOutputStream for OutputStream + * @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 +102,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(); } @@ -179,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) { @@ -196,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) @@ -225,29 +243,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 +315,82 @@ 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 { + logger.warn(String.format("Unsupported constant type %s: %s", v.getType(), v)); + 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; + } 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 + return true; + } + return false; } /** @@ -315,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 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/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/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..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; @@ -324,6 +330,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; } @@ -512,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/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..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<>(); @@ -161,6 +162,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. @@ -840,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()); @@ -1083,10 +1092,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; @@ -1135,23 +1145,49 @@ 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 + 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/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 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()); } }