Skip to content

Commit 425b2b9

Browse files
committed
added support for more complex fragment initialization to fix #283
1 parent eed3fa2 commit 425b2b9

File tree

1 file changed

+149
-5
lines changed

1 file changed

+149
-5
lines changed

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

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Set;
27+
import java.util.concurrent.ExecutionException;
2728

2829
import org.slf4j.Logger;
2930
import org.slf4j.LoggerFactory;
3031

32+
import com.google.common.cache.CacheBuilder;
33+
import com.google.common.cache.CacheLoader;
34+
import com.google.common.cache.LoadingCache;
35+
36+
import heros.solver.Pair;
3137
import soot.AnySubType;
3238
import soot.Body;
3339
import soot.FastHierarchy;
@@ -36,12 +42,17 @@
3642
import soot.RefType;
3743
import soot.Scene;
3844
import soot.SootClass;
45+
import soot.SootField;
3946
import soot.SootMethod;
4047
import soot.SootMethodRef;
4148
import soot.Type;
4249
import soot.Unit;
4350
import soot.Value;
51+
import soot.jimple.ArrayRef;
52+
import soot.jimple.AssignStmt;
53+
import soot.jimple.CastExpr;
4454
import soot.jimple.ClassConstant;
55+
import soot.jimple.FieldRef;
4556
import soot.jimple.IdentityStmt;
4657
import soot.jimple.InstanceInvokeExpr;
4758
import soot.jimple.InvokeExpr;
@@ -59,6 +70,9 @@
5970
import soot.jimple.infoflow.values.IValueProvider;
6071
import soot.jimple.infoflow.values.SimpleConstantValueProvider;
6172
import soot.jimple.toolkits.callgraph.Edge;
73+
import soot.toolkits.graph.ExceptionalUnitGraph;
74+
import soot.toolkits.graph.ExceptionalUnitGraphFactory;
75+
import soot.toolkits.scalar.SimpleLocalDefs;
6276
import soot.util.HashMultiMap;
6377
import soot.util.MultiMap;
6478

@@ -94,10 +108,16 @@ public abstract class AbstractCallbackAnalyzer {
94108

95109
protected final SootClass scSupportViewPager = Scene.v().getSootClassUnsafe("android.support.v4.view.ViewPager");
96110
protected final SootClass scAndroidXViewPager = Scene.v().getSootClassUnsafe("androidx.viewpager.widget.ViewPager");
111+
97112
protected final SootClass scFragmentStatePagerAdapter = Scene.v()
98113
.getSootClassUnsafe("android.support.v4.app.FragmentStatePagerAdapter");
114+
protected final SootClass scFragmentPagerAdapter = Scene.v()
115+
.getSootClassUnsafe("android.support.v4.app.FragmentPagerAdapter");
116+
99117
protected final SootClass scAndroidXFragmentStatePagerAdapter = Scene.v()
100118
.getSootClassUnsafe("androidx.fragment.app.FragmentStatePagerAdapter");
119+
protected final SootClass scAndroidXFragmentPagerAdapter = Scene.v()
120+
.getSootClassUnsafe("androidx.fragment.app.FragmentPagerAdapter");
101121

102122
protected final InfoflowAndroidConfiguration config;
103123
protected final Set<SootClass> entryPointClasses;
@@ -115,6 +135,69 @@ public abstract class AbstractCallbackAnalyzer {
115135

116136
protected IValueProvider valueProvider = new SimpleConstantValueProvider();
117137

138+
protected LoadingCache<SootField, List<Type>> arrayToContentTypes = CacheBuilder.newBuilder()
139+
.build(new CacheLoader<SootField, List<Type>>() {
140+
141+
@Override
142+
public List<Type> load(SootField field) throws Exception {
143+
// Find all assignments to this field
144+
List<Type> typeList = new ArrayList<>();
145+
field.getDeclaringClass().getMethods().stream().filter(m -> m.isConcrete())
146+
.map(m -> m.retrieveActiveBody()).forEach(b -> {
147+
// Find all locals that reference the field
148+
Set<Local> arrayLocals = new HashSet<>();
149+
for (Unit u : b.getUnits()) {
150+
if (u instanceof AssignStmt) {
151+
AssignStmt assignStmt = (AssignStmt) u;
152+
Value rop = assignStmt.getRightOp();
153+
Value lop = assignStmt.getLeftOp();
154+
if (rop instanceof FieldRef && ((FieldRef) rop).getField() == field) {
155+
arrayLocals.add((Local) lop);
156+
} else if (lop instanceof FieldRef && ((FieldRef) lop).getField() == field) {
157+
arrayLocals.add((Local) rop);
158+
}
159+
}
160+
}
161+
162+
// Find casts
163+
for (Unit u : b.getUnits()) {
164+
if (u instanceof AssignStmt) {
165+
AssignStmt assignStmt = (AssignStmt) u;
166+
Value rop = assignStmt.getRightOp();
167+
Value lop = assignStmt.getLeftOp();
168+
169+
if (rop instanceof CastExpr) {
170+
CastExpr ce = (CastExpr) rop;
171+
if (arrayLocals.contains(ce.getOp()))
172+
arrayLocals.add((Local) lop);
173+
else if (arrayLocals.contains(lop))
174+
arrayLocals.add((Local) ce.getOp());
175+
}
176+
}
177+
}
178+
179+
// Find the assignments to the array locals
180+
for (Unit u : b.getUnits()) {
181+
if (u instanceof AssignStmt) {
182+
AssignStmt assignStmt = (AssignStmt) u;
183+
Value rop = assignStmt.getRightOp();
184+
Value lop = assignStmt.getLeftOp();
185+
if (lop instanceof ArrayRef) {
186+
ArrayRef arrayRef = (ArrayRef) lop;
187+
if (arrayLocals.contains(arrayRef.getBase())) {
188+
Type t = rop.getType();
189+
if (t instanceof RefType)
190+
typeList.add(rop.getType());
191+
}
192+
}
193+
}
194+
}
195+
});
196+
return typeList;
197+
}
198+
199+
});
200+
118201
public AbstractCallbackAnalyzer(InfoflowAndroidConfiguration config, Set<SootClass> entryPointClasses)
119202
throws IOException {
120203
this(config, entryPointClasses, "AndroidCallbacks.txt");
@@ -488,15 +571,23 @@ else if (methodName.equals("inflate") && stmt.getInvokeExpr().getArgCount() > 1)
488571
* @author Julius Naeumann
489572
*/
490573
protected void analyzeMethodForViewPagers(SootClass clazz, SootMethod method) {
491-
if (scSupportViewPager == null || scFragmentStatePagerAdapter == null)
492-
if (scAndroidXViewPager == null || scAndroidXFragmentStatePagerAdapter == null)
493-
return;
574+
// We need at least one fragment base class
575+
if (scSupportViewPager == null && scAndroidXViewPager == null)
576+
return;
577+
// We need at least one class with a method to register a fragment
578+
if (scFragmentStatePagerAdapter == null && scAndroidXFragmentStatePagerAdapter == null
579+
&& scFragmentPagerAdapter == null && scAndroidXFragmentPagerAdapter == null)
580+
return;
494581

495582
if (!method.isConcrete())
496583
return;
497584

498585
Body body = method.retrieveActiveBody();
499586

587+
if (method.getDeclaringClass().getName().equals("org.liberty.android.fantastischmemo.ui.AnyMemo")
588+
&& method.getName().equals("initDrawer"))
589+
System.out.println("x");
590+
500591
// look for invocations of ViewPager.setAdapter
501592
for (Unit u : body.getUnits()) {
502593
Stmt stmt = (Stmt) u;
@@ -525,7 +616,8 @@ protected void analyzeMethodForViewPagers(SootClass clazz, SootMethod method) {
525616
RefType rt = (RefType) pa.getType();
526617

527618
// check whether argument is of type FragmentStatePagerAdapter
528-
if (!safeIsType(pa, scFragmentStatePagerAdapter) && !safeIsType(pa, scAndroidXFragmentStatePagerAdapter))
619+
if (!safeIsType(pa, scFragmentStatePagerAdapter) && !safeIsType(pa, scAndroidXFragmentStatePagerAdapter)
620+
&& !safeIsType(pa, scFragmentPagerAdapter) && !safeIsType(pa, scAndroidXFragmentPagerAdapter))
529621
continue;
530622

531623
// now analyze getItem() to find possible Fragments
@@ -546,7 +638,59 @@ protected void analyzeMethodForViewPagers(SootClass clazz, SootMethod method) {
546638
Value rv = rs.getOp();
547639
Type type = rv.getType();
548640
if (type instanceof RefType) {
549-
checkAndAddFragment(method.getDeclaringClass(), ((RefType) type).getSootClass());
641+
SootClass rtClass = ((RefType) type).getSootClass();
642+
if (rv instanceof Local && (rtClass.getName().startsWith("android.")
643+
|| rtClass.getName().startsWith("androidx.")))
644+
analyzeFragmentCandidates(rs, getItem, (Local) rv);
645+
else
646+
checkAndAddFragment(method.getDeclaringClass(), rtClass);
647+
}
648+
}
649+
}
650+
}
651+
}
652+
653+
/**
654+
* Attempts to find fragments that are not returned immediately, but that
655+
* require a more complex backward analysis. This analysis is best-effort, we do
656+
* not attempt to solve every possible case.
657+
*
658+
* @param s The statement at which the fragment is returned
659+
* @param m The method in which the fragment is returned
660+
* @param l The local that contains the fragment
661+
*/
662+
private void analyzeFragmentCandidates(Stmt s, SootMethod m, Local l) {
663+
ExceptionalUnitGraph g = ExceptionalUnitGraphFactory.createExceptionalUnitGraph(m.getActiveBody());
664+
SimpleLocalDefs lds = new SimpleLocalDefs(g);
665+
666+
List<Pair<Local, Stmt>> toSearch = new ArrayList<>();
667+
Set<Pair<Local, Stmt>> doneSet = new HashSet<>();
668+
toSearch.add(new Pair<>(l, s));
669+
670+
while (!toSearch.isEmpty()) {
671+
Pair<Local, Stmt> pair = toSearch.remove(0);
672+
if (doneSet.add(pair)) {
673+
List<Unit> defs = lds.getDefsOfAt(pair.getO1(), pair.getO2());
674+
for (Unit def : defs) {
675+
if (def instanceof AssignStmt) {
676+
AssignStmt assignStmt = (AssignStmt) def;
677+
Value rop = assignStmt.getRightOp();
678+
if (rop instanceof ArrayRef) {
679+
ArrayRef arrayRef = (ArrayRef) rop;
680+
681+
// Look for all assignments to the array
682+
toSearch.add(new Pair<>((Local) arrayRef.getBase(), assignStmt));
683+
} else if (rop instanceof FieldRef) {
684+
FieldRef fieldRef = (FieldRef) rop;
685+
try {
686+
List<Type> typeList = arrayToContentTypes.get(fieldRef.getField());
687+
typeList.stream().map(t -> ((RefType) t).getSootClass())
688+
.forEach(c -> checkAndAddFragment(m.getDeclaringClass(), c));
689+
} catch (ExecutionException e) {
690+
logger.error(String.format("Could not load potential types for field %s",
691+
fieldRef.getField().getSignature()), e);
692+
}
693+
}
550694
}
551695
}
552696
}

0 commit comments

Comments
 (0)