Skip to content

Commit b3d24f9

Browse files
committed
Improve handling of callbacks and dummy main generation
1 parent d76250a commit b3d24f9

6 files changed

Lines changed: 111 additions & 49 deletions

File tree

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -670,8 +670,8 @@ private void calculateCallbackMethods(LayoutFileParser lfp, SootClass component)
670670
// filter out callbacks even if the respective component is only
671671
// analyzed later.
672672
AbstractCallbackAnalyzer jimpleClass = callbackClasses == null
673-
? new DefaultCallbackAnalyzer(config, entryPointClasses, callbackFile)
674-
: new DefaultCallbackAnalyzer(config, entryPointClasses, callbackClasses);
673+
? new DefaultCallbackAnalyzer(config, entryPointClasses, callbackMethods, callbackFile)
674+
: new DefaultCallbackAnalyzer(config, entryPointClasses, callbackMethods, callbackClasses);
675675
if (valueProvider != null)
676676
jimpleClass.setValueProvider(valueProvider);
677677
jimpleClass.addCallbackFilter(new AlienHostComponentFilter(entrypoints));
@@ -736,6 +736,8 @@ private void calculateCallbackMethods(LayoutFileParser lfp, SootClass component)
736736
if (!Scene.v().hasCallGraph())
737737
throw new RuntimeException("No callgraph in Scene even after creating one. That's very sad "
738738
+ "and should never happen.");
739+
740+
lfp.parseLayoutFileDirect(config.getAnalysisFileConfig().getTargetAPKFile());
739741
PackManager.v().getPack("wjtp").apply();
740742

741743
// Creating all callgraph takes time and memory. Check whether
@@ -956,6 +958,7 @@ private boolean collectXmlBasedCallbackMethods(LayoutFileParser lfp, AbstractCal
956958
hasNewCallback = true;
957959
break;
958960
}
961+
959962
SootClass sclass = currentClass.getSuperclassUnsafe();
960963
if (sclass == null) {
961964
logger.error(String.format("Callback method %s not found in class %s", methodName,
@@ -982,7 +985,7 @@ private boolean collectXmlBasedCallbackMethods(LayoutFileParser lfp, AbstractCal
982985
if (controls != null) {
983986
for (AndroidLayoutControl lc : controls) {
984987
if (!SystemClassHandler.v().isClassInSystemPackage(lc.getViewClass().getName()))
985-
registerCallbackMethodsForView(callbackClass, lc);
988+
hasNewCallback |= registerCallbackMethodsForView(callbackClass, lc);
986989
}
987990
}
988991
} else
@@ -1053,19 +1056,20 @@ private void calculateCallbackMethodsFast(LayoutFileParser lfp, SootClass compon
10531056
* @param callbackClass The class with which to associate the layout callbacks
10541057
* @param lc The layout control whose callbacks are to be associated
10551058
* with the given class
1059+
* @return
10561060
*/
1057-
private void registerCallbackMethodsForView(SootClass callbackClass, AndroidLayoutControl lc) {
1061+
private boolean registerCallbackMethodsForView(SootClass callbackClass, AndroidLayoutControl lc) {
10581062
// Ignore system classes
10591063
if (SystemClassHandler.v().isClassInSystemPackage(callbackClass.getName()))
1060-
return;
1064+
return false;
10611065

10621066
// Get common Android classes
10631067
if (scView == null)
10641068
scView = Scene.v().getSootClass("android.view.View");
10651069

10661070
// Check whether the current class is actually a view
10671071
if (!Scene.v().getOrMakeFastHierarchy().canStoreType(lc.getViewClass().getType(), scView.getType()))
1068-
return;
1072+
return false;
10691073

10701074
// There are also some classes that implement interesting callback
10711075
// methods.
@@ -1080,16 +1084,19 @@ private void registerCallbackMethodsForView(SootClass callbackClass, AndroidLayo
10801084
systemMethods.put(sm.getSubSignature(), sm);
10811085
}
10821086

1087+
boolean changed = false;
10831088
// Scan for methods that overwrite parent class methods
10841089
for (SootMethod sm : sc.getMethods()) {
10851090
if (!sm.isConstructor()) {
10861091
SootMethod parentMethod = systemMethods.get(sm.getSubSignature());
1087-
if (parentMethod != null)
1092+
if (parentMethod != null) {
10881093
// This is a real callback method
1089-
this.callbackMethods.put(callbackClass,
1094+
changed |= this.callbackMethods.put(callbackClass,
10901095
new AndroidCallbackDefinition(sm, parentMethod, CallbackType.Widget));
1096+
}
10911097
}
10921098
}
1099+
return changed;
10931100
}
10941101

10951102
/**

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public abstract class AbstractCallbackAnalyzer {
106106
protected final MultiMap<SootClass, Integer> layoutClasses = new HashMultiMap<>();
107107
protected final Set<SootClass> dynamicManifestComponents = new HashSet<>();
108108
protected final MultiMap<SootClass, SootClass> fragmentClasses = new HashMultiMap<>();
109+
protected final MultiMap<SootClass, SootClass> fragmentClassesRev = new HashMultiMap<>();
109110
protected final Map<SootClass, Integer> fragmentIDs = new HashMap<>();
110111

111112
protected final List<ICallbackFilter> callbackFilters = new ArrayList<>();
@@ -615,12 +616,12 @@ protected boolean invokesSetContentView(InvokeExpr inv) {
615616
|| curClass.getName().equals("android.support.v7.app.AppCompatActivity")
616617
|| curClass.getName().equals("androidx.appcompat.app.AppCompatActivity"))
617618
return true;
618-
// As long as the class is subclass of android.app.Activity,
619-
// it can be sure that the setContentView method is what we expected.
620-
// Following 2 statements make the overriding of method
621-
// setContentView ignored.
619+
// As long as the class is subclass of android.app.Activity,
620+
// it can be sure that the setContentView method is what we expected.
621+
// Following 2 statements make the overriding of method
622+
// setContentView ignored.
622623
// if (curClass.declaresMethod("void setContentView(int)"))
623-
// return false;
624+
// return false;
624625
curClass = curClass.hasSuperclass() ? curClass.getSuperclass() : null;
625626
}
626627
return false;
@@ -646,7 +647,7 @@ protected boolean invokesInflate(InvokeExpr inv) {
646647
if (curClass.getName().equals("android.app.Fragment"))
647648
return true;
648649
if (curClass.declaresMethod("android.view.View inflate(int,android.view.ViewGroup,boolean)"))
649-
return false;
650+
return true;
650651
curClass = curClass.hasSuperclass() ? curClass.getSuperclass() : null;
651652
}
652653
return false;
@@ -814,6 +815,7 @@ protected boolean checkAndAddMethod(SootMethod method, SootMethod parentMethod,
814815
*/
815816
protected void checkAndAddFragment(SootClass componentClass, SootClass fragmentClass) {
816817
this.fragmentClasses.put(componentClass, fragmentClass);
818+
this.fragmentClassesRev.put(fragmentClass, componentClass);
817819
}
818820

819821
private boolean isEmpty(Body activeBody) {

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

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
import java.util.Map;
1111
import java.util.Set;
1212

13+
import heros.solver.Pair;
1314
import soot.MethodOrMethodContext;
1415
import soot.PackManager;
16+
import soot.RefType;
1517
import soot.Scene;
1618
import soot.SceneTransformer;
1719
import soot.SootClass;
@@ -48,22 +50,28 @@ public class DefaultCallbackAnalyzer extends AbstractCallbackAnalyzer implements
4850
private AndroidEntryPointUtils entryPointUtils = new AndroidEntryPointUtils();
4951
private Set<IMemoryBoundedSolverStatusNotification> notificationListeners = new HashSet<>();
5052
private ISolverTerminationReason isKilled = null;
53+
private MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks;
5154

5255
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses)
5356
throws IOException {
5457
super(config, entryPointClasses);
5558
}
5659

5760
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses,
58-
String callbackFile) throws IOException {
61+
MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks, String callbackFile) throws IOException {
5962
super(config, entryPointClasses, callbackFile);
63+
this.viewCallbacks = viewCallbacks;
6064
}
6165

6266
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses,
63-
Set<String> androidCallbacks) throws IOException {
67+
MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks, Set<String> androidCallbacks)
68+
throws IOException {
6469
super(config, entryPointClasses, androidCallbacks);
70+
this.viewCallbacks = viewCallbacks;
6571
}
6672

73+
QueueReader<MethodOrMethodContext> reachableChangedListener;
74+
6775
/**
6876
* Collects the callback methods for all Android default handlers implemented in
6977
* the source code. Note that this operation runs inside Soot, so this method
@@ -100,17 +108,31 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
100108
getLifecycleMethods(sc));
101109

102110
// Check for callbacks registered in the code
103-
analyzeRechableMethods(sc, methods);
111+
analyzeReachableMethods(sc, methods);
104112

105113
// Check for method overrides
106114
analyzeMethodOverrideCallbacks(sc);
107115
analyzeClassInterfaceCallbacks(sc, sc, sc);
108116
}
117+
reachableChangedListener = Scene.v().getReachableMethods().listener();
109118
logger.info("Callback analysis done.");
110119
} else {
111120
// Incremental mode, only process the worklist
112121
logger.info(String.format("Running incremental callback analysis for %d components...",
113122
callbackWorklist.size()));
123+
// Find the mappings between classes and layouts
124+
findClassLayoutMappings();
125+
126+
MultiMap<SootMethod, SootClass> reverseViewCallbacks = new HashMultiMap<>();
127+
for (Pair<SootClass, AndroidCallbackDefinition> i : viewCallbacks)
128+
reverseViewCallbacks.put(i.getO2().getTargetMethod(), i.getO1());
129+
while (reachableChangedListener.hasNext()) {
130+
SootMethod m = reachableChangedListener.next().method();
131+
Set<SootClass> o = reverseViewCallbacks.get(m);
132+
for (SootClass i : o) {
133+
callbackWorklist.put(i, m);
134+
}
135+
}
114136

115137
MultiMap<SootClass, SootMethod> workList = new HashMultiMap<>(callbackWorklist);
116138
for (Iterator<SootClass> it = workList.keySet().iterator(); it.hasNext();) {
@@ -122,6 +144,10 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
122144
Set<SootMethod> callbacks = callbackWorklist.get(componentClass);
123145
callbackWorklist.remove(componentClass);
124146

147+
Set<SootClass> activityComponents = fragmentClassesRev.get(componentClass);
148+
if (activityComponents == null || activityComponents.isEmpty())
149+
activityComponents = Collections.singleton(componentClass);
150+
125151
// Check whether we're already beyond the maximum number
126152
// of callbacks for the current component
127153
if (config.getCallbackConfig().getMaxCallbacksPerComponent() > 0
@@ -133,15 +159,21 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
133159

134160
// Check for method overrides. The whole class might be new.
135161
analyzeMethodOverrideCallbacks(componentClass);
136-
analyzeClassInterfaceCallbacks(componentClass, componentClass, componentClass);
162+
for (SootClass activityComponent : activityComponents) {
163+
if (activityComponent == null)
164+
activityComponent = componentClass;
165+
analyzeClassInterfaceCallbacks(componentClass, componentClass, activityComponent);
166+
}
137167

138168
// Collect all methods that we need to analyze
139169
List<MethodOrMethodContext> entryClasses = new ArrayList<>(callbacks.size());
140-
for (SootMethod sm : callbacks)
141-
entryClasses.add(sm);
170+
for (SootMethod sm : callbacks) {
171+
if (sm != null)
172+
entryClasses.add(sm);
173+
}
142174

143175
// Check for further callback declarations
144-
analyzeRechableMethods(componentClass, entryClasses);
176+
analyzeReachableMethods(componentClass, entryClasses);
145177
}
146178
logger.info("Incremental callback analysis done.");
147179
}
@@ -226,7 +258,7 @@ private static Collection<? extends MethodOrMethodContext> getLifecycleMethods(S
226258
return lifecycleMethods;
227259
}
228260

229-
private void analyzeRechableMethods(SootClass lifecycleElement, List<MethodOrMethodContext> methods) {
261+
private void analyzeReachableMethods(SootClass lifecycleElement, List<MethodOrMethodContext> methods) {
230262
// Make sure to exclude all other edges in the callgraph except for the
231263
// edges start in the lifecycle methods we explicitly pass in
232264
ComponentReachableMethods rm = new ComponentReachableMethods(config, lifecycleElement, methods);
@@ -279,40 +311,53 @@ protected void checkAndAddFragment(SootClass componentClass, SootClass fragmentC
279311
}
280312
}
281313

314+
Iterator<MethodOrMethodContext> rmIterator;
315+
282316
/**
283317
* Finds the mappings between classes and their respective layout files
284318
*/
285319
private void findClassLayoutMappings() {
286-
Iterator<MethodOrMethodContext> rmIterator = Scene.v().getReachableMethods().listener();
320+
if (rmIterator == null)
321+
rmIterator = Scene.v().getReachableMethods().listener();
287322
while (rmIterator.hasNext()) {
288323
SootMethod sm = rmIterator.next().method();
324+
289325
if (!sm.isConcrete())
290326
continue;
291327
if (SystemClassHandler.v().isClassInSystemPackage(sm.getDeclaringClass().getName()))
292328
continue;
293-
294-
for (Unit u : sm.retrieveActiveBody().getUnits())
329+
RefType fragmentType = RefType.v("android.app.Fragment");
330+
for (Unit u : sm.retrieveActiveBody().getUnits()) {
295331
if (u instanceof Stmt) {
296332
Stmt stmt = (Stmt) u;
297333
if (stmt.containsInvokeExpr()) {
298334
InvokeExpr inv = stmt.getInvokeExpr();
299-
if (invokesSetContentView(inv) || invokesInflate(inv)) { // check
300-
// also
301-
// for
302-
// inflate
303-
// to
304-
// look
305-
// for
306-
// the
307-
// fragments
335+
if (invokesSetContentView(inv)) { // check
336+
// also
337+
// for
338+
// inflate
339+
// to
340+
// look
341+
// for
342+
// the
343+
// fragments
308344
for (Value val : inv.getArgs()) {
309345
Integer intValue = valueProvider.getValue(sm, stmt, val, Integer.class);
310-
if (intValue != null)
346+
if (intValue != null) {
311347
this.layoutClasses.put(sm.getDeclaringClass(), intValue);
348+
}
349+
350+
}
351+
}
352+
if (invokesInflate(inv)) {
353+
Integer intValue = valueProvider.getValue(sm, stmt, inv.getArg(0), Integer.class);
354+
if (intValue != null) {
355+
this.layoutClasses.put(sm.getDeclaringClass(), intValue);
312356
}
313357
}
314358
}
315359
}
360+
}
316361
}
317362
}
318363

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ private void findClassLayoutMappings() {
7777
if (invokesSetContentView(inv)) {
7878
for (Value val : inv.getArgs()) {
7979
Integer intValue = valueProvider.getValue(sm, stmt, val, Integer.class);
80-
if (intValue != null)
80+
if (intValue != null) {
8181
this.layoutClasses.put(sm.getDeclaringClass(), intValue);
82+
}
8283
}
8384
}
8485
}

soot-infoflow-android/src/soot/jimple/infoflow/android/callbacks/filters/UnreachableConstructorFilter.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package soot.jimple.infoflow.android.callbacks.filters;
22

3+
import soot.RefType;
4+
import soot.Scene;
35
import soot.SootClass;
46
import soot.SootMethod;
57

@@ -21,6 +23,11 @@ public boolean accepts(SootClass component, SootClass callbackHandler) {
2123
// If the callback is in the component class itself, it is trivially reachable
2224
if (component == callbackHandler)
2325
return true;
26+
RefType fragmentType = RefType.v("android.app.Fragment");
27+
boolean isFragment = Scene.v().getFastHierarchy().canStoreType(callbackHandler.getType(), fragmentType);
28+
if (isFragment)
29+
// we cannot find constructors for these...
30+
return true;
2431

2532
{
2633
SootClass curHandler = callbackHandler;

soot-infoflow/src/soot/jimple/infoflow/memory/FlowDroidTimeoutWatcher.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ public class FlowDroidTimeoutWatcher implements IMemoryBoundedSolverStatusNotifi
2525
*
2626
*/
2727
private enum SolverState {
28-
/**
29-
* The solver has not been started yet
30-
*/
31-
IDLE,
32-
/**
33-
* The solver is running
34-
*/
35-
RUNNING,
36-
/**
37-
* The solver has completed its work
38-
*/
39-
DONE
28+
/**
29+
* The solver has not been started yet
30+
*/
31+
IDLE,
32+
/**
33+
* The solver is running
34+
*/
35+
RUNNING,
36+
/**
37+
* The solver has completed its work
38+
*/
39+
DONE
4040
}
4141

4242
private final Logger logger = LoggerFactory.getLogger(getClass());
@@ -120,7 +120,7 @@ public void run() {
120120

121121
// If things have not stopped on their own account, we force
122122
// them to
123-
if (!stopped & !allTerminated) {
123+
if (!stopped && !allTerminated) {
124124
logger.warn("Timeout reached, stopping the solvers...");
125125
if (results != null)
126126
results.addException("Timeout reached");

0 commit comments

Comments
 (0)