Skip to content

Commit 4e5d8ac

Browse files
committed
Merge branch 'develop' of github.com:MarcMil/FlowDroid into faster-sysclass
2 parents 86c8762 + 4df7437 commit 4e5d8ac

3 files changed

Lines changed: 225 additions & 71 deletions

File tree

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

Lines changed: 13 additions & 8 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;
@@ -127,10 +128,10 @@ public abstract class AbstractCallbackAnalyzer {
127128

128129
protected final MultiMap<SootClass, AndroidCallbackDefinition> callbackMethods = new HashMultiMap<>();
129130
protected final MultiMap<SootClass, Integer> layoutClasses = new HashMultiMap<>();
130-
protected final Set<SootClass> dynamicManifestComponents = new HashSet<>();
131+
protected final Set<SootClass> dynamicManifestComponents = Collections.newSetFromMap(new ConcurrentHashMap<>());
131132
protected final MultiMap<SootClass, SootClass> fragmentClasses = new HashMultiMap<>();
132133
protected final MultiMap<SootClass, SootClass> fragmentClassesRev = new HashMultiMap<>();
133-
protected final Map<SootClass, Integer> fragmentIDs = new HashMap<>();
134+
protected final Map<SootClass, Integer> fragmentIDs = new ConcurrentHashMap<>();
134135

135136
protected final List<ICallbackFilter> callbackFilters = new ArrayList<>();
136137
protected final Set<SootClass> excludedEntryPoints = new HashSet<>();
@@ -381,7 +382,7 @@ protected void checkAndAddCallback(Set<SootClass> callbackClasses, Type argType)
381382
* @return True if all filters accept the given component-callback mapping,
382383
* otherwise false
383384
*/
384-
private boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass) {
385+
protected boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass) {
385386
for (ICallbackFilter filter : callbackFilters)
386387
if (!filter.accepts(lifecycleElement, targetClass))
387388
return false;
@@ -397,7 +398,7 @@ private boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass)
397398
* @return True if all filters accept the given component-callback mapping,
398399
* otherwise false
399400
*/
400-
private boolean filterAccepts(SootClass lifecycleElement, SootMethod targetMethod) {
401+
protected boolean filterAccepts(SootClass lifecycleElement, SootMethod targetMethod) {
401402
for (ICallbackFilter filter : callbackFilters)
402403
if (!filter.accepts(lifecycleElement, targetMethod))
403404
return false;
@@ -453,7 +454,9 @@ protected void analyzeMethodForJavascriptInterfaces(SootMethod method) {
453454
final SootMethodRef methodRef = iexpr.getMethodRef();
454455
if (methodRef.getName().equals("addJavascriptInterface") && iexpr.getArgCount() == 2
455456
&& fastHierarchy.canStoreType(methodRef.getDeclaringClass().getType(), webViewType)) {
456-
this.javaScriptInterfaces.put(method, stmt);
457+
synchronized (javaScriptInterfaces) {
458+
this.javaScriptInterfaces.put(method, stmt);
459+
}
457460
}
458461
}
459462
}
@@ -990,8 +993,10 @@ && isEmpty(method.retrieveActiveBody()))
990993
if (!filterAccepts(lifecycleClass, method))
991994
return false;
992995

993-
return this.callbackMethods.put(lifecycleClass,
994-
new AndroidCallbackDefinition(method, parentMethod, callbackType));
996+
synchronized (this.callbackMethods) {
997+
return this.callbackMethods.put(lifecycleClass,
998+
new AndroidCallbackDefinition(method, parentMethod, callbackType));
999+
}
9951000
}
9961001

9971002
/**
@@ -1001,7 +1006,7 @@ && isEmpty(method.retrieveActiveBody()))
10011006
* fragment belongs
10021007
* @param fragmentClass The fragment class
10031008
*/
1004-
protected void checkAndAddFragment(SootClass componentClass, SootClass fragmentClass) {
1009+
protected synchronized void checkAndAddFragment(SootClass componentClass, SootClass fragmentClass) {
10051010
this.fragmentClasses.put(componentClass, fragmentClass);
10061011
this.fragmentClassesRev.put(fragmentClass, componentClass);
10071012
}

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

Lines changed: 113 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.util.List;
99
import java.util.Map;
1010
import java.util.Set;
11+
import java.util.concurrent.Executors;
12+
import java.util.concurrent.TimeUnit;
1113

1214
import heros.solver.Pair;
1315
import soot.MethodOrMethodContext;
@@ -28,6 +30,7 @@
2830
import soot.jimple.infoflow.android.entryPointCreators.AndroidEntryPointUtils;
2931
import soot.jimple.infoflow.memory.IMemoryBoundedSolver;
3032
import soot.jimple.infoflow.memory.ISolverTerminationReason;
33+
import soot.jimple.infoflow.util.ReusableExecutor;
3134
import soot.jimple.infoflow.util.SystemClassHandler;
3235
import soot.util.HashMultiMap;
3336
import soot.util.MultiMap;
@@ -93,23 +96,34 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
9396

9497
// Find the mappings between classes and layouts
9598
findClassLayoutMappings();
96-
97-
// Process the callback classes directly reachable from the
98-
// entry points
99-
for (SootClass sc : entryPointClasses) {
100-
// Check whether we're still running
101-
if (isKilled != null)
102-
break;
103-
104-
List<MethodOrMethodContext> methods = new ArrayList<MethodOrMethodContext>(
105-
entryPointUtils.getLifecycleMethods(sc));
106-
107-
// Check for callbacks registered in the code
108-
analyzeReachableMethods(sc, methods);
109-
110-
// Check for method overrides
111-
analyzeMethodOverrideCallbacks(sc);
112-
analyzeClassInterfaceCallbacks(sc, sc, sc);
99+
ReusableExecutor executionService = new ReusableExecutor(
100+
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
101+
102+
try {
103+
// Process the callback classes directly reachable from the
104+
// entry points
105+
for (SootClass sc : entryPointClasses) {
106+
// Check whether we're still running
107+
if (isKilled != null)
108+
break;
109+
110+
List<MethodOrMethodContext> methods = new ArrayList<MethodOrMethodContext>(
111+
entryPointUtils.getLifecycleMethods(sc));
112+
113+
// Check for callbacks registered in the code
114+
analyzeReachableMethods(executionService, sc, methods);
115+
116+
// Check for method overrides
117+
analyzeMethodOverrideCallbacks(sc);
118+
analyzeClassInterfaceCallbacks(sc, sc, sc);
119+
}
120+
} finally {
121+
executionService.shutdown();
122+
try {
123+
executionService.awaitTermination(1, TimeUnit.DAYS);
124+
} catch (InterruptedException e) {
125+
logger.error("Interrupted searching for callbacks");
126+
}
113127
}
114128
reachableChangedListener = Scene.v().getReachableMethods().listener();
115129
logger.info("Callback analysis done.");
@@ -132,47 +146,58 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
132146
// Incremental mode, only process the worklist
133147
logger.info(String.format("Running incremental callback analysis for %d components...",
134148
callbackWorklist.size()));
149+
ReusableExecutor executionService = new ReusableExecutor(
150+
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
135151

136152
MultiMap<SootClass, SootMethod> workList = new HashMultiMap<>(callbackWorklist);
137-
for (Iterator<SootClass> it = workList.keySet().iterator(); it.hasNext();) {
138-
// Check whether we're still running
139-
if (isKilled != null)
140-
break;
141-
142-
SootClass componentClass = it.next();
143-
Set<SootMethod> callbacks = callbackWorklist.get(componentClass);
144-
callbackWorklist.remove(componentClass);
145-
146-
Set<SootClass> activityComponents = fragmentClassesRev.get(componentClass);
147-
if (activityComponents == null || activityComponents.isEmpty())
148-
activityComponents = Collections.singleton(componentClass);
149-
150-
// Check whether we're already beyond the maximum number
151-
// of callbacks for the current component
152-
if (config.getCallbackConfig().getMaxCallbacksPerComponent() > 0
153-
&& callbacks.size() > config.getCallbackConfig().getMaxCallbacksPerComponent()) {
154-
callbackMethods.remove(componentClass);
155-
entryPointClasses.remove(componentClass);
156-
continue;
157-
}
153+
try {
154+
for (Iterator<SootClass> it = workList.keySet().iterator(); it.hasNext();) {
155+
// Check whether we're still running
156+
if (isKilled != null)
157+
break;
158+
159+
SootClass componentClass = it.next();
160+
Set<SootMethod> callbacks = callbackWorklist.get(componentClass);
161+
callbackWorklist.remove(componentClass);
162+
163+
Set<SootClass> activityComponents = fragmentClassesRev.get(componentClass);
164+
if (activityComponents == null || activityComponents.isEmpty())
165+
activityComponents = Collections.singleton(componentClass);
166+
167+
// Check whether we're already beyond the maximum number
168+
// of callbacks for the current component
169+
if (config.getCallbackConfig().getMaxCallbacksPerComponent() > 0
170+
&& callbacks.size() > config.getCallbackConfig().getMaxCallbacksPerComponent()) {
171+
callbackMethods.remove(componentClass);
172+
entryPointClasses.remove(componentClass);
173+
continue;
174+
}
158175

159-
// Check for method overrides. The whole class might be new.
160-
analyzeMethodOverrideCallbacks(componentClass);
161-
for (SootClass activityComponent : activityComponents) {
162-
if (activityComponent == null)
163-
activityComponent = componentClass;
164-
analyzeClassInterfaceCallbacks(componentClass, componentClass, activityComponent);
165-
}
176+
// Check for method overrides. The whole class might be new.
177+
analyzeMethodOverrideCallbacks(componentClass);
178+
for (SootClass activityComponent : activityComponents) {
179+
if (activityComponent == null)
180+
activityComponent = componentClass;
181+
analyzeClassInterfaceCallbacks(componentClass, componentClass, activityComponent);
182+
}
166183

167-
// Collect all methods that we need to analyze
168-
List<MethodOrMethodContext> entryClasses = new ArrayList<>(callbacks.size());
169-
for (SootMethod sm : callbacks) {
170-
if (sm != null)
171-
entryClasses.add(sm);
172-
}
184+
// Collect all methods that we need to analyze
185+
List<MethodOrMethodContext> entryClasses = new ArrayList<>(callbacks.size());
186+
for (SootMethod sm : callbacks) {
187+
if (sm != null)
188+
entryClasses.add(sm);
189+
}
173190

174-
// Check for further callback declarations
175-
analyzeReachableMethods(componentClass, entryClasses);
191+
// Check for further callback declarations
192+
analyzeReachableMethods(executionService, componentClass, entryClasses);
193+
}
194+
} finally {
195+
executionService.shutdown();
196+
try {
197+
executionService.awaitTermination(1, TimeUnit.DAYS);
198+
} catch (InterruptedException e) {
199+
logger.error("Interrupted searching for callbacks");
200+
}
176201
}
177202
logger.info("Incremental callback analysis done.");
178203
}
@@ -186,32 +211,57 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
186211
PackManager.v().getPack("wjtp").add(transform);
187212
}
188213

189-
private void analyzeReachableMethods(SootClass lifecycleElement, List<MethodOrMethodContext> methods) {
214+
@Override
215+
protected boolean filterAccepts(SootClass lifecycleElement, SootClass targetClass) {
216+
return super.filterAccepts(lifecycleElement, targetClass);
217+
}
218+
219+
@Override
220+
protected boolean filterAccepts(SootClass lifecycleElement, SootMethod targetMethod) {
221+
return super.filterAccepts(lifecycleElement, targetMethod);
222+
}
223+
224+
protected void analyzeReachableMethods(ReusableExecutor executionService, SootClass lifecycleElement,
225+
List<MethodOrMethodContext> methods) {
190226
// Make sure to exclude all other edges in the callgraph except for the
191227
// edges start in the lifecycle methods we explicitly pass in
192228
ComponentReachableMethods rm = new ComponentReachableMethods(config, lifecycleElement, methods);
193229
rm.update();
194230

195231
// Scan for listeners in the class hierarchy
196232
QueueReader<MethodOrMethodContext> reachableMethods = rm.listener();
233+
for (ICallbackFilter filter : callbackFilters)
234+
filter.setReachableMethods(rm);
235+
197236
while (reachableMethods.hasNext()) {
198237
// Check whether we're still running
199238
if (isKilled != null)
200239
break;
201240

202-
for (ICallbackFilter filter : callbackFilters)
203-
filter.setReachableMethods(rm);
204-
205241
SootMethod method = reachableMethods.next().method();
206242
if (method.isConcrete()) {
207-
analyzeMethodForCallbackRegistrations(lifecycleElement, method);
208-
analyzeMethodForDynamicBroadcastReceiver(method);
209-
analyzeMethodForServiceConnection(method);
210-
analyzeMethodForFragmentTransaction(lifecycleElement, method);
211-
analyzeMethodForViewPagers(lifecycleElement, method);
212-
analyzeMethodForJavascriptInterfaces(method);
243+
executionService.execute(new Runnable() {
244+
245+
@Override
246+
public void run() {
247+
analyzeMethodForCallbackRegistrations(lifecycleElement, method);
248+
analyzeMethodForDynamicBroadcastReceiver(method);
249+
analyzeMethodForServiceConnection(method);
250+
analyzeMethodForFragmentTransaction(lifecycleElement, method);
251+
analyzeMethodForViewPagers(lifecycleElement, method);
252+
analyzeMethodForJavascriptInterfaces(method);
253+
}
254+
255+
});
213256
}
214257
}
258+
try {
259+
//We need to wait here since the callback filters have the reachable methods, that
260+
//depend on the current component
261+
executionService.waitUntilFinished();
262+
} catch (InterruptedException e) {
263+
throw new RuntimeException(e);
264+
}
215265
}
216266

217267
@Override

0 commit comments

Comments
 (0)