package edu.psu.cse.siis.ic3;
import java.io.IOException;
import java.io.Writer;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import soot.Scene;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import edu.psu.cse.siis.coal.AnalysisParameters;
import edu.psu.cse.siis.coal.Constants;
import edu.psu.cse.siis.coal.Model;
import edu.psu.cse.siis.coal.PropagationTimers;
import edu.psu.cse.siis.coal.Result;
import edu.psu.cse.siis.coal.Results;
import edu.psu.cse.siis.coal.arguments.Argument;
import edu.psu.cse.siis.coal.field.values.FieldValue;
import edu.psu.cse.siis.coal.field.values.ScalarFieldValue;
import edu.psu.cse.siis.coal.field.values.TopFieldValue;
import edu.psu.cse.siis.coal.values.BasePropagationValue;
import edu.psu.cse.siis.coal.values.BottomPropagationValue;
import edu.psu.cse.siis.coal.values.PathValue;
import edu.psu.cse.siis.coal.values.PropagationValue;
import edu.psu.cse.siis.coal.values.TopPropagationValue;
import edu.psu.cse.siis.ic3.db.DbConnection;
import edu.psu.cse.siis.ic3.db.SQLConnection;
import edu.psu.cse.siis.ic3.manifest.ManifestComponent;
import edu.psu.cse.siis.ic3.manifest.ManifestData;
import edu.psu.cse.siis.ic3.manifest.ManifestIntentFilter;
public class ResultProcessor {
private final Logger logger = LoggerFactory.getLogger(getClass());
private static final String ENTRY_POINT_INTENT = "<INTENT>";
private final int[] preciseNonLinking = { 0, 0, 0, 0 };
private final int[] preciseLinking = { 0, 0, 0, 0 };
private final int[] imprecise = { 0, 0, 0, 0, 0 };
private final int[] top = { 0, 0, 0 };
private final int[] bottom = { 0, 0, 0 };
private final int[] nonexistent = { 0, 0, 0 };
private final int[] preciseFieldValueCount = { 0, 0, 0 };
private final int[] partiallyPreciseFieldValueCount = { 0, 0, 0 };
private final int[] impreciseFieldValueCount = { 0, 0, 0 };
private int intentWithData = 0;
private int providerArgument = 0;
public void processResult(boolean writeToDb, String appName,
Map<String, Integer> componentToIdMap, int analysisClassesCount, Writer writer)
throws IOException, SQLException {
for (Result result : Results.getResults()) {
((Ic3Result) result).dump();
analyzeResult(result);
if (writeToDb) {
writeResultToDb(result, componentToIdMap);
}
}
if (writeToDb) {
SQLConnection.closeConnection();
}
Timers.v().totalTimer.end();
String statistics =
appName + " " + analysisClassesCount + " " + PropagationTimers.v().reachableMethods + " "
+ PropagationTimers.v().reachableStatements + " " + preciseNonLinking[0] + " "
+ preciseNonLinking[3] + " " + preciseNonLinking[1] + " " + preciseNonLinking[2] + " "
+ preciseLinking[0] + " " + preciseLinking[3] + " " + preciseLinking[1] + " "
+ preciseLinking[2] + " " + imprecise[0] + " " + imprecise[3] + " " + imprecise[1]
+ " " + imprecise[2] + " " + bottom[0] + " " + bottom[1] + " " + bottom[2] + " "
+ top[0] + " " + top[1] + " " + top[2] + " " + nonexistent[0] + " " + nonexistent[1]
+ " " + nonexistent[2] + " " + providerArgument + " " + imprecise[4] + " "
+ PropagationTimers.v().pathValues + " " + PropagationTimers.v().separatePathValues
+ " " + PropagationTimers.v().modelParsing.getTime() + " "
+ Timers.v().mainGeneration.getTime() + " " + Timers.v().entryPointMapping.getTime()
+ " " + Timers.v().classLoading.getTime() + " "
+ PropagationTimers.v().problemGeneration.getTime() + " "
+ PropagationTimers.v().ideSolution.getTime() + " "
+ PropagationTimers.v().valueComposition.getTime() + " "
+ PropagationTimers.v().resultGeneration.getTime() + " "
+ (PropagationTimers.v().soot.getTime() - PropagationTimers.v().totalTimer.getTime())
+ " " + (Timers.v().misc.getTime() + PropagationTimers.v().misc.getTime()) + " "
/* + PropagationTimers.v().argumentValueTime + " " */+ Timers.v().totalTimer.getTime()
+ "\n";
if (logger.isInfoEnabled()) {
logger.info(statistics);
}
if (writer != null) {
writer.write(statistics);
writer.close();
}
}
@SuppressWarnings("unchecked")
private void writeResultToDb(Result result, Map<String, Integer> componentToIdMap)
throws SQLException {
for (Map.Entry<Unit, Map<Integer, Object>> entry : result.getResults().entrySet()) {
Unit unit = entry.getKey();
Argument[] arguments = Model.v().getArgumentsForQuery((Stmt) unit);
Map<SootMethod, Set<String>> entryPointMap = ((Ic3Result) result).getEntryPointMap();
if (arguments != null) {
SootMethod method = AnalysisParameters.v().getIcfg().getMethodOf(unit);
int unitId = getIdForUnit(unit, method);
Map<String, Object> valueMap = new HashMap<>(arguments.length);
Map<Integer, Object> argnumToValueMap = entry.getValue();
for (Argument argument : arguments) {
valueMap.put(argument.getProperty("valueType"),
argnumToValueMap.get(argument.getArgnum()[0]));
}
String className = method.getDeclaringClass().getName();
String methodSignature = method.getSignature();
if (valueMap.containsKey("activity")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
(BasePropagationValue) valueMap.get("activity"),
edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.ACTIVITY, null, null,
entryPointMap.get(method), componentToIdMap);
} else if (valueMap.containsKey("service")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
(BasePropagationValue) valueMap.get("service"),
edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.SERVICE, null, null,
entryPointMap.get(method), componentToIdMap);
} else if (valueMap.containsKey("receiver")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
(BasePropagationValue) valueMap.get("receiver"),
edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.RECEIVER,
(Set<String>) valueMap.get("permission"), null, entryPointMap.get(method),
componentToIdMap);
} else if (valueMap.containsKey("intentFilter")) {
insertDynamicReceiver((Set<String>) valueMap.get("permission"),
(Set<String>) valueMap.get("receiverType"),
(BasePropagationValue) valueMap.get("intentFilter"), method, unit);
} else if (valueMap.containsKey("provider")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
(BasePropagationValue) valueMap.get("provider"),
edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.PROVIDER, null, null,
entryPointMap.get(method), componentToIdMap);
} else if (valueMap.containsKey("authority")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
getUriValueForAuthorities((Set<String>) valueMap.get("authority")),
edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.PROVIDER, null, null,
entryPointMap.get(method), componentToIdMap);
} else if (valueMap.containsKey("pendingIntent")) {
BasePropagationValue basePropagationValue =
(BasePropagationValue) valueMap.get("pendingIntent");
String targetType =
basePropagationValue instanceof PropagationValue ? (String) ((PropagationValue) basePropagationValue)
.getValuesForField("targetType").iterator().next().getValue()
: null;
Set<String> permissions = (Set<String>) valueMap.get("permission");
if (targetType != null) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
basePropagationValue, targetType, permissions, null, entryPointMap.get(method),
componentToIdMap);
} else {
for (String target : Arrays.asList("a", "r", "s")) {
DbConnection.insertIntentAtExitPoint(className, methodSignature, unitId,
basePropagationValue, target, permissions, null, entryPointMap.get(method),
componentToIdMap);
}
}
} else if (valueMap.containsKey("componentExtra")) {
DbConnection.insertComponentExtras(entryPointMap.get(method), componentToIdMap,
(Set<String>) valueMap.get("componentExtra"));
}
}
}
}
private void insertDynamicReceiver(Set<String> permissions, Set<String> receiverTypes,
BasePropagationValue intentFilters, SootMethod method, Unit unit) throws SQLException {
if (permissions == null) {
permissions = Collections.singleton(null);
}
for (String receiverType : receiverTypes) {
for (String permission : permissions) {
insertDynamicReceiverHelper(permission, receiverType, intentFilters, method, unit);
}
}
}
private void insertDynamicReceiverHelper(String permission, String receiverType,
BasePropagationValue intentFilters, SootMethod method, Unit unit) throws SQLException {
Integer missingIntentFilters;
Set<ManifestIntentFilter> manifestIntentFilters;
if (intentFilters == null || intentFilters instanceof TopPropagationValue
|| intentFilters instanceof BottomPropagationValue) {
missingIntentFilters = 0;
manifestIntentFilters = null;
} else if (intentFilters instanceof PropagationValue) {
missingIntentFilters = null;
PropagationValue propagationValue = (PropagationValue) intentFilters;
manifestIntentFilters = new HashSet<>();
for (PathValue branchValue : propagationValue.getPathValues()) {
Integer filterPriority = null;
FieldValue priorityFieldValue = branchValue.getFieldValue("priority");
if (priorityFieldValue != null) {
filterPriority = (Integer) priorityFieldValue.getValue();
}
manifestIntentFilters.add(new ManifestIntentFilter(branchValue
.getSetStringFieldValue("actions"), branchValue.getSetStringFieldValue("categories"),
false, makeManifestData(branchValue), filterPriority));
}
} else {
throw new RuntimeException("Unknown intent filter type: " + intentFilters.getClass());
}
ManifestComponent manifestComponent =
new ManifestComponent(edu.psu.cse.siis.ic3.db.Constants.ComponentShortType.RECEIVER,
receiverType, true, true, permission, null, missingIntentFilters, method, unit);
manifestComponent.setIntentFilters(manifestIntentFilters);
SQLConnection.insertIntentFilters(Collections.singletonList(manifestComponent));
}
private List<ManifestData> makeManifestData(PathValue branchValue) {
Set<String> mimeTypes = branchValue.getSetStringFieldValue("dataType");
Set<DataAuthority> authorities =
branchValue.getSetFieldValue("authorities", DataAuthority.class);
Set<String> paths = branchValue.getSetStringFieldValue("paths");
Set<String> schemes = branchValue.getSetStringFieldValue("schemes");
if (mimeTypes == null && authorities == null && paths == null && schemes == null) {
return null;
}
if (mimeTypes == null) {
mimeTypes = Collections.singleton(null);
}
if (authorities == null) {
authorities = Collections.singleton(new DataAuthority(null, null));
}
if (paths == null) {
paths = Collections.singleton(null);
}
if (schemes == null) {
schemes = Collections.singleton(null);
}
List<ManifestData> result = new ArrayList<>();
for (String mimeType : mimeTypes) {
for (DataAuthority dataAuthority : authorities) {
for (String dataPath : paths) {
for (String scheme : schemes) {
result.add(new ManifestData(scheme, dataAuthority.getHost(), dataAuthority.getPort(),
dataPath, mimeType));
}
}
}
}
return result;
}
private BasePropagationValue getUriValueForAuthorities(Set<String> authorities) {
if (authorities == null) {
return null;
}
PropagationValue collectingValue = new PropagationValue();
for (String authority : authorities) {
PathValue branchValue = new PathValue();
ScalarFieldValue schemeFieldValue = new ScalarFieldValue("content");
branchValue.addFieldEntry("scheme", schemeFieldValue);
ScalarFieldValue authorityFieldValue = new ScalarFieldValue(authority);
branchValue.addFieldEntry("authority", authorityFieldValue);
collectingValue.addPathValue(branchValue);
}
return collectingValue;
}
private int getIdForUnit(Unit unit, SootMethod method) {
int id = 0;
for (Unit currentUnit : method.getActiveBody().getUnits()) {
if (currentUnit == unit) {
return id;
}
++id;
}
return -1;
}
@SuppressWarnings("unchecked")
private void analyzeResult(Result result) {
Set<String> nonLinkingFieldNames = new HashSet<>();
nonLinkingFieldNames.add("extras");
nonLinkingFieldNames.add("flags");
nonLinkingFieldNames.add("fragment");
nonLinkingFieldNames.add("query");
for (Map.Entry<Unit, Map<Integer, Object>> entry0 : result.getResults().entrySet()) {
Collection<Object> argumentValues = entry0.getValue().values();
boolean top = false;
boolean bottom = false;
// This is true only if the linking field are precisely known.
boolean preciseLinking = true;
// This is true only if all fields are precisely known.
boolean preciseNonLinking = true;
boolean nonexistent = false;
boolean intentWithUri = false;
boolean entryPointIntent = false;
int resultIndex = getResultIndex((Stmt) entry0.getKey());
for (Object value2 : argumentValues) {
if (value2 == null) {
nonexistent = true;
} else if (value2 instanceof TopPropagationValue) {
top = true;
} else if (value2 instanceof BottomPropagationValue) {
bottom = true;
} else if (value2 instanceof PropagationValue) {
// System.out.println(value2);
Set<PathValue> pathValues = ((PropagationValue) value2).getPathValues();
PropagationTimers.v().pathValues += pathValues.size();
// This keeps track of all the fields that are defined across all paths.
Set<String> definedFields = new HashSet<>();
for (PathValue pathValue : pathValues) {
definedFields.addAll(pathValue.getFieldMap().keySet());
}
Map<String, Set<FieldValue>> separateFieldValues = new HashMap<>();
for (PathValue branchValue : pathValues) {
intentWithUri = intentWithUri || isIntentWithUri(branchValue.getFieldMap());
Set<String> definedFieldsInPath = new HashSet<>();
for (Map.Entry<String, FieldValue> entry : branchValue.getFieldMap().entrySet()) {
String fieldName = entry.getKey();
FieldValue fieldValue = entry.getValue();
addValueToSetMap(fieldName, fieldValue, separateFieldValues);
definedFieldsInPath.add(fieldName);
if (fieldValue instanceof TopFieldValue) {
if (nonLinkingFieldNames.contains(fieldName)) {
preciseNonLinking = false;
} else {
preciseNonLinking = false;
preciseLinking = false;
}
} else {
Object value = fieldValue.getValue();
if (value == null) {
continue;
}
if (value instanceof Set) {
Set<Object> values = (Set<Object>) value;
if (values.contains(Constants.ANY_STRING) || values.contains(Constants.ANY_CLASS)
|| values.contains(Constants.ANY_INT) || values.contains(ENTRY_POINT_INTENT)
|| values.contains("top")) {
if (values.contains(ENTRY_POINT_INTENT)) {
entryPointIntent = true;
}
preciseNonLinking = false;
if (!nonLinkingFieldNames.contains(fieldName)) {
preciseLinking = false;
}
}
} else {
if (value.equals(Constants.ANY_STRING) || value.equals(Constants.ANY_CLASS)
|| value.equals(Constants.ANY_INT) || value.equals(ENTRY_POINT_INTENT)
|| value.equals("top")) {
if (value.equals(ENTRY_POINT_INTENT)) {
entryPointIntent = true;
}
preciseNonLinking = false;
if (!nonLinkingFieldNames.contains(fieldName)) {
preciseLinking = false;
}
}
}
}
}
// If some field is undefined in some path, then we need to keep track of a null value
// for that field.
Set<String> undefinedFieldForPath = new HashSet<>(definedFields);
undefinedFieldForPath.removeAll(definedFieldsInPath);
for (String fieldName : undefinedFieldForPath) {
addValueToSetMap(fieldName, null, separateFieldValues);
}
}
int separateCount = 1;
for (Set<FieldValue> values : separateFieldValues.values()) {
separateCount *= values.size();
}
// System.out.println(separateCount);
PropagationTimers.v().separatePathValues += separateCount;
}
}
if (intentWithUri) {
++this.intentWithData;
}
if (nonexistent) {
if (Scene
.v()
.getActiveHierarchy()
.isClassSubclassOfIncluding(
AnalysisParameters.v().getIcfg().getMethodOf(entry0.getKey()).getDeclaringClass(),
Scene.v().getSootClass("android.content.ContentProvider"))) {
++this.providerArgument;
} else {
++this.nonexistent[resultIndex];
}
} else if (top) {
++this.top[resultIndex];
} else if (bottom) {
++this.bottom[resultIndex];
} else if (preciseNonLinking) {
if (intentWithUri) {
++this.preciseNonLinking[3];
} else {
++this.preciseNonLinking[resultIndex];
}
} else if (preciseLinking) {
if (intentWithUri) {
++this.preciseLinking[3];
} else {
++this.preciseLinking[resultIndex];
}
} else {
if (entryPointIntent) {
++this.imprecise[4];
} else if (intentWithUri) {
++this.imprecise[3];
} else {
++this.imprecise[resultIndex];
}
}
}
}
private void addValueToSetMap(String key, FieldValue value, Map<String, Set<FieldValue>> map) {
Set<FieldValue> separateValuesForField = map.get(key);
if (separateValuesForField == null) {
separateValuesForField = new HashSet<>();
map.put(key, separateValuesForField);
}
separateValuesForField.add(value);
}
private boolean isIntentWithUri(Map<String, FieldValue> fieldMap) {
Set<String> fields = fieldMap.keySet();
if (fields.contains("action") || fields.contains("categories")) {
if ((fields.contains("uri") && fieldMap.get("uri") != null && fieldMap.get("uri").getValue() != null)
|| (fields.contains("path") && fieldMap.get("path") != null && fieldMap.get("path")
.getValue() != null)
|| (fields.contains("scheme") && fieldMap.get("scheme") != null && fieldMap.get("scheme")
.getValue() != null)
|| (fields.contains("ssp") && fieldMap.get("ssp") != null && fieldMap.get("ssp")
.getValue() != null)) {
return true;
}
}
return false;
}
private int getResultIndex(Stmt stmt) {
InvokeExpr invokeExpr = stmt.getInvokeExpr();
List<Type> types = invokeExpr.getMethod().getParameterTypes();
for (Type type : types) {
if (type.toString().equals("android.content.IntentFilter")) {
return 1;
} else if (type.toString().equals("android.net.Uri")) {
return 2;
}
}
return 0;
}
private boolean containsPartialDefinition(Set<Object> values) {
for (Object value : values) {
if (value instanceof String && ((String) value).contains("(.*)")) {
return true;
}
}
return false;
}
}