Skip to content

Commit b33697e

Browse files
authored
Merge pull request #837 from MarcMil/faster-sysclass
Search for callbacks in parallel & faster system class handling
2 parents 4c8481b + 80a0636 commit b33697e

17 files changed

Lines changed: 363 additions & 190 deletions

soot-infoflow-android/src/soot/jimple/infoflow/android/SetupApplication.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,9 @@ private void calculateCallbackMethods(LayoutFileParser lfp, SootClass component)
801801
// Creating all callgraph takes time and memory. Check whether
802802
// the solver has been aborted in the meantime
803803
if (jimpleClass instanceof IMemoryBoundedSolver) {
804-
if (((IMemoryBoundedSolver) jimpleClass).isKilled()) {
805-
logger.warn("Aborted callback collection because of low memory");
804+
IMemoryBoundedSolver imb = ((IMemoryBoundedSolver) jimpleClass);
805+
if (imb.isKilled()) {
806+
logger.warn("Aborted callback collection because of " + imb.getTerminationReason().toString());
806807
break;
807808
}
808809
}

soot-infoflow-android/src/soot/jimple/infoflow/android/callbacks/AbstractCallbackAnalyzer.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.List;
2626
import java.util.Map;
2727
import java.util.Set;
28+
import java.util.concurrent.ConcurrentHashMap;
2829
import java.util.concurrent.ExecutionException;
2930

3031
import org.slf4j.Logger;
@@ -75,6 +76,7 @@
7576
import soot.toolkits.graph.ExceptionalUnitGraph;
7677
import soot.toolkits.graph.ExceptionalUnitGraphFactory;
7778
import soot.toolkits.scalar.SimpleLocalDefs;
79+
import soot.util.ConcurrentHashMultiMap;
7880
import soot.util.HashMultiMap;
7981
import soot.util.MultiMap;
8082

@@ -127,10 +129,10 @@ public abstract class AbstractCallbackAnalyzer {
127129

128130
protected final MultiMap<SootClass, AndroidCallbackDefinition> callbackMethods = new HashMultiMap<>();
129131
protected final MultiMap<SootClass, Integer> layoutClasses = new HashMultiMap<>();
130-
protected final Set<SootClass> dynamicManifestComponents = new HashSet<>();
131-
protected final MultiMap<SootClass, SootClass> fragmentClasses = new HashMultiMap<>();
132-
protected final MultiMap<SootClass, SootClass> fragmentClassesRev = new HashMultiMap<>();
133-
protected final Map<SootClass, Integer> fragmentIDs = new HashMap<>();
132+
protected final Set<SootClass> dynamicManifestComponents = Collections.newSetFromMap(new ConcurrentHashMap<>());
133+
protected final MultiMap<SootClass, SootClass> fragmentClasses = new ConcurrentHashMultiMap<>();
134+
protected final MultiMap<SootClass, SootClass> fragmentClassesRev = new ConcurrentHashMultiMap<>();
135+
protected final Map<SootClass, Integer> fragmentIDs = new ConcurrentHashMap<>();
134136

135137
protected final List<ICallbackFilter> callbackFilters = new ArrayList<>();
136138
protected final Set<SootClass> excludedEntryPoints = new HashSet<>();
@@ -200,7 +202,7 @@ else if (arrayLocals.contains(lop))
200202

201203
});
202204

203-
private MultiMap<SootMethod, Stmt> javaScriptInterfaces = new HashMultiMap<SootMethod, Stmt>();
205+
private MultiMap<SootMethod, Stmt> javaScriptInterfaces = new ConcurrentHashMultiMap<SootMethod, Stmt>();
204206

205207
public AbstractCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses)
206208
throws IOException {
@@ -381,7 +383,7 @@ protected void checkAndAddCallback(Set<SootClass> callbackClasses, Type argType)
381383
* @return True if all filters accept the given component-callback mapping,
382384
* otherwise false
383385
*/
384-
private boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass) {
386+
protected boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass) {
385387
for (ICallbackFilter filter : callbackFilters)
386388
if (!filter.accepts(lifecycleElement, targetClass))
387389
return false;
@@ -397,7 +399,7 @@ private boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass)
397399
* @return True if all filters accept the given component-callback mapping,
398400
* otherwise false
399401
*/
400-
private boolean filterAccepts(SootClass lifecycleElement, SootMethod targetMethod) {
402+
protected boolean filterAccepts(SootClass lifecycleElement, SootMethod targetMethod) {
401403
for (ICallbackFilter filter : callbackFilters)
402404
if (!filter.accepts(lifecycleElement, targetMethod))
403405
return false;

soot-infoflow-android/src/soot/jimple/infoflow/android/callbacks/ComponentReachableMethods.java

Lines changed: 68 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
11
package soot.jimple.infoflow.android.callbacks;
22

33
import java.util.Collection;
4-
import java.util.HashSet;
4+
import java.util.Collections;
55
import java.util.Iterator;
66
import java.util.Set;
7+
import java.util.concurrent.ConcurrentHashMap;
78

9+
import soot.FastHierarchy;
810
import soot.Kind;
911
import soot.MethodOrMethodContext;
1012
import soot.RefType;
1113
import soot.Scene;
1214
import soot.SootClass;
1315
import soot.SootMethod;
1416
import soot.jimple.InstanceInvokeExpr;
17+
import soot.jimple.InvokeExpr;
1518
import soot.jimple.infoflow.android.InfoflowAndroidConfiguration;
1619
import soot.jimple.infoflow.util.SystemClassHandler;
20+
import soot.jimple.toolkits.callgraph.CallGraph;
1721
import soot.jimple.toolkits.callgraph.Edge;
1822
import soot.jimple.toolkits.callgraph.EdgePredicate;
1923
import soot.jimple.toolkits.callgraph.Filter;
24+
import soot.jimple.toolkits.callgraph.ReachableMethods;
2025
import soot.jimple.toolkits.callgraph.Targets;
2126
import soot.util.queue.ChunkedQueue;
2227
import soot.util.queue.QueueReader;
@@ -37,10 +42,11 @@ public class ComponentReachableMethods {
3742

3843
private final InfoflowAndroidConfiguration config;
3944
private final SootClass originalComponent;
40-
private final Set<MethodOrMethodContext> set = new HashSet<MethodOrMethodContext>();
45+
protected final Set<MethodOrMethodContext> set = Collections.newSetFromMap(new ConcurrentHashMap<>());
4146
private final ChunkedQueue<MethodOrMethodContext> reachables = new ChunkedQueue<MethodOrMethodContext>();
4247
private final QueueReader<MethodOrMethodContext> allReachables = reachables.reader();
4348
private QueueReader<MethodOrMethodContext> unprocessedMethods;
49+
private final SystemClassHandler systemClassHandler = SystemClassHandler.v();
4450

4551
/**
4652
* Creates a new instance of the {@link ComponentReachableMethods} class
@@ -67,67 +73,79 @@ private void addMethods(Iterator<MethodOrMethodContext> methods) {
6773

6874
private void addMethod(MethodOrMethodContext m) {
6975
// Filter out methods in system classes
70-
if (!SystemClassHandler.v().isClassInSystemPackage(m.method().getDeclaringClass())) {
76+
if (!systemClassHandler.isClassInSystemPackage(m.method().getDeclaringClass())) {
7177
if (set.add(m)) {
7278
reachables.add(m);
7379
}
7480
}
7581
}
7682

7783
public void update() {
78-
while (unprocessedMethods.hasNext()) {
79-
MethodOrMethodContext m = unprocessedMethods.next();
80-
Filter filter = new Filter(new EdgePredicate() {
84+
final Scene sc = Scene.v();
85+
final FastHierarchy fh = Scene.v().getFastHierarchy();
86+
final RefType runnable = RefType.v("java.lang.Runnable");
87+
final EdgePredicate predicate = new EdgePredicate() {
88+
89+
@Override
90+
public boolean want(Edge e) {
91+
if (e.kind() == Kind.CLINIT)
92+
return false;
93+
else if (e.kind() == Kind.VIRTUAL) {
94+
// We only filter calls to this.*
95+
InvokeExpr inv = e.srcStmt().getInvokeExprUnsafe();
96+
if (!e.src().isStatic() && inv instanceof InstanceInvokeExpr) {
97+
SootMethod refMethod = inv.getMethod();
98+
InstanceInvokeExpr iinv = (InstanceInvokeExpr) inv;
99+
if (iinv.getBase() == e.src().getActiveBody().getThisLocal()) {
100+
101+
// If our parent class P has an abstract
102+
// method foo() and the lifecycle
103+
// class L overrides foo(), make sure that
104+
// all calls to P.foo() in the
105+
// context of L only go to L.foo().
106+
SootClass calleeClass = refMethod.getDeclaringClass();
107+
if (fh.isSubclass(originalComponent, calleeClass)) {
108+
SootClass targetClass = e.getTgt().method().getDeclaringClass();
109+
return targetClass == originalComponent
110+
|| fh.isSubclass(targetClass, originalComponent);
111+
}
112+
}
81113

82-
@Override
83-
public boolean want(Edge e) {
84-
if (e.kind() == Kind.CLINIT)
114+
// We do not expect callback registrations in
115+
// any
116+
// calls to system classes
117+
if (systemClassHandler.isClassInSystemPackage(refMethod.getDeclaringClass()))
118+
return false;
119+
}
120+
} else if (config.getCallbackConfig().getFilterThreadCallbacks()) {
121+
// Check for thread call edges
122+
if (e.kind() == Kind.THREAD || e.kind() == Kind.EXECUTOR)
85123
return false;
86-
else if (e.kind() == Kind.VIRTUAL) {
87-
// We only filter calls to this.*
88-
if (!e.src().isStatic() && e.srcStmt().getInvokeExpr() instanceof InstanceInvokeExpr) {
89-
SootMethod refMethod = e.srcStmt().getInvokeExpr().getMethod();
90-
InstanceInvokeExpr iinv = (InstanceInvokeExpr) e.srcStmt().getInvokeExpr();
91-
if (iinv.getBase() == e.src().getActiveBody().getThisLocal()) {
92-
93-
// If our parent class P has an abstract
94-
// method foo() and the lifecycle
95-
// class L overrides foo(), make sure that
96-
// all calls to P.foo() in the
97-
// context of L only go to L.foo().
98-
SootClass calleeClass = refMethod.getDeclaringClass();
99-
if (Scene.v().getFastHierarchy().isSubclass(originalComponent, calleeClass)) {
100-
SootClass targetClass = e.getTgt().method().getDeclaringClass();
101-
return targetClass == originalComponent
102-
|| Scene.v().getFastHierarchy().isSubclass(targetClass, originalComponent);
103-
}
104-
}
105124

106-
// We do not expect callback registrations in
107-
// any
108-
// calls to system classes
109-
if (SystemClassHandler.v().isClassInSystemPackage(refMethod.getDeclaringClass()))
110-
return false;
111-
}
112-
} else if (config.getCallbackConfig().getFilterThreadCallbacks()) {
113-
// Check for thread call edges
114-
if (e.kind() == Kind.THREAD || e.kind() == Kind.EXECUTOR)
125+
// Some apps have a custom layer for managing
126+
// threads,
127+
// so we need a more generic model
128+
if (e.tgt().getName().equals("run"))
129+
if (sc.getFastHierarchy().canStoreType(e.tgt().getDeclaringClass().getType(), runnable))
115130
return false;
131+
}
132+
return true;
133+
}
116134

117-
// Some apps have a custom layer for managing
118-
// threads,
119-
// so we need a more generic model
120-
if (e.tgt().getName().equals("run"))
121-
if (Scene.v().getFastHierarchy().canStoreType(e.tgt().getDeclaringClass().getType(),
122-
RefType.v("java.lang.Runnable")))
123-
return false;
124-
}
125-
return true;
135+
};
136+
final CallGraph cg = sc.getCallGraph();
137+
138+
while (unprocessedMethods.hasNext()) {
139+
MethodOrMethodContext m = unprocessedMethods.next();
140+
Iterator<Edge> of = cg.edgesOutOf(m);
141+
if (of != null && of.hasNext()) {
142+
Filter filter = new Filter(predicate);
143+
Iterator<Edge> targets = filter.wrap(of);
144+
if (targets.hasNext()) {
145+
addMethods(new Targets(targets));
126146
}
147+
}
127148

128-
});
129-
Iterator<Edge> targets = filter.wrap(Scene.v().getCallGraph().edgesOutOf(m));
130-
addMethods(new Targets(targets));
131149
}
132150
}
133151

@@ -159,4 +177,4 @@ public int size() {
159177
return set.size();
160178
}
161179

162-
}
180+
}

0 commit comments

Comments
 (0)