Skip to content

Commit cf9a87b

Browse files
authored
Merge pull request #347 from MarcMil/dev-experimental
Improve handling of callbacks and dummy main generation
2 parents b091315 + 9a694e6 commit cf9a87b

6 files changed

Lines changed: 101 additions & 39 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: 9 additions & 8 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;
@@ -643,10 +644,9 @@ protected boolean invokesInflate(InvokeExpr inv) {
643644
// of using the superclass signature
644645
SootClass curClass = inv.getMethod().getDeclaringClass();
645646
while (curClass != null) {
646-
if (curClass.getName().equals("android.app.Fragment"))
647+
if (curClass.getName().equals("android.app.Fragment")
648+
|| curClass.getName().equals("android.view.LayoutInflater"))
647649
return true;
648-
if (curClass.declaresMethod("android.view.View inflate(int,android.view.ViewGroup,boolean)"))
649-
return false;
650650
curClass = curClass.hasSuperclass() ? curClass.getSuperclass() : null;
651651
}
652652
return false;
@@ -814,6 +814,7 @@ protected boolean checkAndAddMethod(SootMethod method, SootMethod parentMethod,
814814
*/
815815
protected void checkAndAddFragment(SootClass componentClass, SootClass fragmentClass) {
816816
this.fragmentClasses.put(componentClass, fragmentClass);
817+
this.fragmentClassesRev.put(fragmentClass, componentClass);
817818
}
818819

819820
private boolean isEmpty(Body activeBody) {

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

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
import java.io.IOException;
44
import java.util.ArrayList;
5+
import java.util.Collections;
56
import java.util.HashSet;
67
import java.util.Iterator;
78
import java.util.List;
89
import java.util.Map;
910
import java.util.Set;
1011

12+
import heros.solver.Pair;
1113
import soot.MethodOrMethodContext;
1214
import soot.PackManager;
15+
import soot.RefType;
1316
import soot.Scene;
1417
import soot.SceneTransformer;
1518
import soot.SootClass;
@@ -45,22 +48,28 @@ public class DefaultCallbackAnalyzer extends AbstractCallbackAnalyzer implements
4548
private AndroidEntryPointUtils entryPointUtils = new AndroidEntryPointUtils();
4649
private Set<IMemoryBoundedSolverStatusNotification> notificationListeners = new HashSet<>();
4750
private ISolverTerminationReason isKilled = null;
51+
private MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks;
4852

4953
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses)
5054
throws IOException {
5155
super(config, entryPointClasses);
5256
}
5357

5458
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses,
55-
String callbackFile) throws IOException {
59+
MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks, String callbackFile) throws IOException {
5660
super(config, entryPointClasses, callbackFile);
61+
this.viewCallbacks = viewCallbacks;
5762
}
5863

5964
public DefaultCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses,
60-
Set<String> androidCallbacks) throws IOException {
65+
MultiMap<SootClass, AndroidCallbackDefinition> viewCallbacks, Set<String> androidCallbacks)
66+
throws IOException {
6167
super(config, entryPointClasses, androidCallbacks);
68+
this.viewCallbacks = viewCallbacks;
6269
}
6370

71+
QueueReader<MethodOrMethodContext> reachableChangedListener;
72+
6473
/**
6574
* Collects the callback methods for all Android default handlers implemented in
6675
* the source code. Note that this operation runs inside Soot, so this method
@@ -97,17 +106,31 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
97106
entryPointUtils.getLifecycleMethods(sc));
98107

99108
// Check for callbacks registered in the code
100-
analyzeRechableMethods(sc, methods);
109+
analyzeReachableMethods(sc, methods);
101110

102111
// Check for method overrides
103112
analyzeMethodOverrideCallbacks(sc);
104113
analyzeClassInterfaceCallbacks(sc, sc, sc);
105114
}
115+
reachableChangedListener = Scene.v().getReachableMethods().listener();
106116
logger.info("Callback analysis done.");
107117
} else {
108118
// Incremental mode, only process the worklist
109119
logger.info(String.format("Running incremental callback analysis for %d components...",
110120
callbackWorklist.size()));
121+
// Find the mappings between classes and layouts
122+
findClassLayoutMappings();
123+
124+
MultiMap<SootMethod, SootClass> reverseViewCallbacks = new HashMultiMap<>();
125+
for (Pair<SootClass, AndroidCallbackDefinition> i : viewCallbacks)
126+
reverseViewCallbacks.put(i.getO2().getTargetMethod(), i.getO1());
127+
while (reachableChangedListener.hasNext()) {
128+
SootMethod m = reachableChangedListener.next().method();
129+
Set<SootClass> o = reverseViewCallbacks.get(m);
130+
for (SootClass i : o) {
131+
callbackWorklist.put(i, m);
132+
}
133+
}
111134

112135
MultiMap<SootClass, SootMethod> workList = new HashMultiMap<>(callbackWorklist);
113136
for (Iterator<SootClass> it = workList.keySet().iterator(); it.hasNext();) {
@@ -119,6 +142,10 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
119142
Set<SootMethod> callbacks = callbackWorklist.get(componentClass);
120143
callbackWorklist.remove(componentClass);
121144

145+
Set<SootClass> activityComponents = fragmentClassesRev.get(componentClass);
146+
if (activityComponents == null || activityComponents.isEmpty())
147+
activityComponents = Collections.singleton(componentClass);
148+
122149
// Check whether we're already beyond the maximum number
123150
// of callbacks for the current component
124151
if (config.getCallbackConfig().getMaxCallbacksPerComponent() > 0
@@ -130,15 +157,21 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
130157

131158
// Check for method overrides. The whole class might be new.
132159
analyzeMethodOverrideCallbacks(componentClass);
133-
analyzeClassInterfaceCallbacks(componentClass, componentClass, componentClass);
160+
for (SootClass activityComponent : activityComponents) {
161+
if (activityComponent == null)
162+
activityComponent = componentClass;
163+
analyzeClassInterfaceCallbacks(componentClass, componentClass, activityComponent);
164+
}
134165

135166
// Collect all methods that we need to analyze
136167
List<MethodOrMethodContext> entryClasses = new ArrayList<>(callbacks.size());
137-
for (SootMethod sm : callbacks)
138-
entryClasses.add(sm);
168+
for (SootMethod sm : callbacks) {
169+
if (sm != null)
170+
entryClasses.add(sm);
171+
}
139172

140173
// Check for further callback declarations
141-
analyzeRechableMethods(componentClass, entryClasses);
174+
analyzeReachableMethods(componentClass, entryClasses);
142175
}
143176
logger.info("Incremental callback analysis done.");
144177
}
@@ -152,7 +185,7 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
152185
PackManager.v().getPack("wjtp").add(transform);
153186
}
154187

155-
private void analyzeRechableMethods(SootClass lifecycleElement, List<MethodOrMethodContext> methods) {
188+
private void analyzeReachableMethods(SootClass lifecycleElement, List<MethodOrMethodContext> methods) {
156189
// Make sure to exclude all other edges in the callgraph except for the
157190
// edges start in the lifecycle methods we explicitly pass in
158191
ComponentReachableMethods rm = new ComponentReachableMethods(config, lifecycleElement, methods);
@@ -205,40 +238,53 @@ protected void checkAndAddFragment(SootClass componentClass, SootClass fragmentC
205238
}
206239
}
207240

241+
Iterator<MethodOrMethodContext> rmIterator;
242+
208243
/**
209244
* Finds the mappings between classes and their respective layout files
210245
*/
211246
private void findClassLayoutMappings() {
212-
Iterator<MethodOrMethodContext> rmIterator = Scene.v().getReachableMethods().listener();
247+
if (rmIterator == null)
248+
rmIterator = Scene.v().getReachableMethods().listener();
213249
while (rmIterator.hasNext()) {
214250
SootMethod sm = rmIterator.next().method();
251+
215252
if (!sm.isConcrete())
216253
continue;
217254
if (SystemClassHandler.v().isClassInSystemPackage(sm.getDeclaringClass().getName()))
218255
continue;
219-
220-
for (Unit u : sm.retrieveActiveBody().getUnits())
256+
RefType fragmentType = RefType.v("android.app.Fragment");
257+
for (Unit u : sm.retrieveActiveBody().getUnits()) {
221258
if (u instanceof Stmt) {
222259
Stmt stmt = (Stmt) u;
223260
if (stmt.containsInvokeExpr()) {
224261
InvokeExpr inv = stmt.getInvokeExpr();
225-
if (invokesSetContentView(inv) || invokesInflate(inv)) { // check
226-
// also
227-
// for
228-
// inflate
229-
// to
230-
// look
231-
// for
232-
// the
233-
// fragments
262+
if (invokesSetContentView(inv)) { // check
263+
// also
264+
// for
265+
// inflate
266+
// to
267+
// look
268+
// for
269+
// the
270+
// fragments
234271
for (Value val : inv.getArgs()) {
235272
Integer intValue = valueProvider.getValue(sm, stmt, val, Integer.class);
236-
if (intValue != null)
273+
if (intValue != null) {
237274
this.layoutClasses.put(sm.getDeclaringClass(), intValue);
275+
}
276+
277+
}
278+
}
279+
if (invokesInflate(inv)) {
280+
Integer intValue = valueProvider.getValue(sm, stmt, inv.getArg(0), Integer.class);
281+
if (intValue != null) {
282+
this.layoutClasses.put(sm.getDeclaringClass(), intValue);
238283
}
239284
}
240285
}
241286
}
287+
}
242288
}
243289
}
244290

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void run() {
122122

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

0 commit comments

Comments
 (0)