Skip to content

Commit 9a7b5ec

Browse files
authored
Merge pull request #863 from MarcMil/fix-flowdroid-bug
Split locals prior to running FlowDroid
2 parents 6bcfe3c + 579042f commit 9a7b5ec

11 files changed

Lines changed: 308 additions & 21 deletions

File tree

soot-infoflow-android/src/soot/jimple/infoflow/android/source/AndroidSourceSinkManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,12 @@ protected Collection<ISourceSinkDefinition> getSinkDefinitions(Stmt sCallSite, I
440440
final String subSig = callee.getSubSignature();
441441
final SootClass sc = callee.getDeclaringClass();
442442

443+
if (sc == null) {
444+
logger.warn(
445+
String.format("%s was not declared; was called at %s", callee.getSubSignature(), sCallSite));
446+
return sinkDefs;
447+
}
448+
443449
// Do not consider ICC methods as sinks if only the base object is
444450
// tainted
445451
boolean isParamTainted = false;

soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/generator/SummaryInfoflow.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.Collection;
44

5+
import soot.Local;
6+
import soot.jimple.infoflow.FlowDroidLocalSplitter;
57
import soot.jimple.infoflow.Infoflow;
68
import soot.jimple.infoflow.InfoflowManager;
79
import soot.jimple.infoflow.solver.IInfoflowSolver;
@@ -35,10 +37,28 @@ public InfoflowManager getManager() {
3537

3638
@Override
3739
protected void onTaintPropagationCompleted(IInfoflowSolver forwardSolver, IInfoflowSolver aliasSolver,
38-
IInfoflowSolver backwardSolver, IInfoflowSolver backwardAliasSolver) {
40+
IInfoflowSolver backwardSolver, IInfoflowSolver backwardAliasSolver) {
3941
cachedManager = this.manager;
4042
}
4143

44+
@Override
45+
protected void unsplitAllBodies() {
46+
//Since we are interested in abstractions, we must not unsplit.
47+
//This is fine for our use case
48+
}
49+
50+
@Override
51+
protected FlowDroidLocalSplitter getLocalSplitter() {
52+
return new FlowDroidLocalSplitter() {
53+
54+
@Override
55+
protected Local createClonedLocal(Local oldLocal) {
56+
//We want "normal" locals since we take deep looks into abstractions
57+
return (Local) oldLocal.clone();
58+
}
59+
};
60+
}
61+
4262
@Override
4363
protected void initializeSoot(String appPath, String libPath, Collection<String> classes) {
4464
this.libPath = libPath;

soot-infoflow/src/soot/jimple/infoflow/AbstractInfoflow.java

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@
5555
import soot.jimple.InvokeExpr;
5656
import soot.jimple.Jimple;
5757
import soot.jimple.Stmt;
58+
import soot.jimple.infoflow.FlowDroidLocalSplitter.SplittedLocal;
5859
import soot.jimple.infoflow.InfoflowConfiguration.AccessPathConfiguration;
60+
import soot.jimple.infoflow.InfoflowConfiguration.AliasingAlgorithm;
5961
import soot.jimple.infoflow.InfoflowConfiguration.CallgraphAlgorithm;
6062
import soot.jimple.infoflow.InfoflowConfiguration.CodeEliminationMode;
6163
import soot.jimple.infoflow.InfoflowConfiguration.DataFlowDirection;
@@ -495,10 +497,11 @@ protected void constructCallgraph() {
495497
// Allow the ICC manager to change the Soot Scene before we continue
496498
if (ipcManager != null)
497499
ipcManager.updateJimpleForICC();
500+
if (config.getAliasingAlgorithm() == AliasingAlgorithm.PtsBased) {
498501

499-
// We might need to patch invokedynamic instructions
500-
if (config.isPatchInvokeDynamicInstructions())
501-
patchDynamicInvokeInstructions();
502+
}
503+
504+
patchCode();
502505

503506
// Run the preprocessors
504507
for (PreAnalysisHandler tr : preProcessors)
@@ -512,11 +515,12 @@ protected void constructCallgraph() {
512515

513516
// To cope with broken APK files, we convert all classes that are still
514517
// dangling after resolution into phantoms
515-
for (SootClass sc : Scene.v().getClasses())
518+
for (SootClass sc : Scene.v().getClasses()) {
516519
if (sc.resolvingLevel() == SootClass.DANGLING) {
517520
sc.setResolvingLevel(SootClass.BODIES);
518521
sc.setPhantomClass();
519522
}
523+
}
520524

521525
// We explicitly select the packs we want to run for performance
522526
// reasons. Do not re-run the callgraph algorithm if the host
@@ -539,20 +543,20 @@ protected void constructCallgraph() {
539543
}
540544

541545
/**
542-
* Re-writes dynamic invocation instructions into traditional invcations
546+
* Inserts patch-code logic
543547
*/
544-
private void patchDynamicInvokeInstructions() {
548+
private void patchCode() {
545549
for (SootClass sc : Scene.v().getClasses()) {
546550
for (SootMethod sm : sc.getMethods()) {
547551
if (sm.hasActiveBody()) {
548552
Body body = sm.getActiveBody();
549-
patchDynamicInvokeInstructions(body);
553+
patchCode(body);
550554
} else if (!(sm.getSource() instanceof MethodSourceInjector) && sm.getSource() != null) {
551555
sm.setSource(new MethodSourceInjector(sm.getSource()) {
552556

553557
@Override
554558
protected void onMethodSourceLoaded(SootMethod m, Body b) {
555-
patchDynamicInvokeInstructions(b);
559+
patchCode(b);
556560
}
557561

558562
});
@@ -561,6 +565,17 @@ protected void onMethodSourceLoaded(SootMethod m, Body b) {
561565
}
562566
}
563567

568+
private void patchCode(Body body) {
569+
if (config.isPatchInvokeDynamicInstructions()) {
570+
patchDynamicInvokeInstructions(body);
571+
}
572+
getLocalSplitter().transform(body);
573+
}
574+
575+
protected FlowDroidLocalSplitter getLocalSplitter() {
576+
return FlowDroidLocalSplitter.v();
577+
}
578+
564579
/**
565580
* Patches the dynamic invocation instructions in the given method body
566581
*
@@ -911,8 +926,14 @@ protected void runAnalysis(final ISourceSinkManager sourcesSinks, final Set<Stri
911926
IInfoflowCFG iCfg = icfgFactory.buildBiDirICFG(config.getCallgraphAlgorithm(),
912927
config.getEnableExceptionTracking());
913928

914-
if (config.isTaintAnalysisEnabled())
915-
runTaintAnalysis(sourcesSinks, additionalSeeds, iCfg, performanceData);
929+
if (config.isTaintAnalysisEnabled()) {
930+
splitAllBodies(Scene.v().getReachableMethods().listener());
931+
try {
932+
runTaintAnalysis(sourcesSinks, additionalSeeds, iCfg, performanceData);
933+
} finally {
934+
unsplitAllBodies();
935+
}
936+
}
916937

917938
// Gather performance data
918939
performanceData.setTotalRuntimeSeconds((int) Math.round((System.nanoTime() - beforeCallgraph) / 1E9));
@@ -939,6 +960,48 @@ protected void runAnalysis(final ISourceSinkManager sourcesSinks, final Set<Stri
939960
}
940961
}
941962

963+
protected void unsplitAllBodies() {
964+
for (SootClass sc : Scene.v().getClasses()) {
965+
for (SootMethod m : sc.getMethods()) {
966+
if (m.hasActiveBody()) {
967+
//We could use the local packer here, but we know exactly what was being split
968+
//so we can be faster here
969+
Body body = m.getActiveBody();
970+
Iterator<ValueBox> it = body.getUseAndDefBoxesIterator();
971+
while (it.hasNext()) {
972+
ValueBox box = it.next();
973+
Value val = box.getValue();
974+
if (val instanceof SplittedLocal) {
975+
SplittedLocal l = (SplittedLocal) val;
976+
box.setValue(l.getOriginalLocal());
977+
}
978+
}
979+
Iterator<Local> lit = body.getLocals().iterator();
980+
while (lit.hasNext()) {
981+
if (lit.next() instanceof SplittedLocal) {
982+
lit.remove();
983+
}
984+
}
985+
}
986+
}
987+
}
988+
}
989+
990+
//With newer soot versions, locals are reused more often, which
991+
//can be a problem for FlowDroid. So, we split the locals prior to
992+
//running FlowDroid.
993+
protected void splitAllBodies(Iterator<? extends MethodOrMethodContext> it) {
994+
FlowDroidLocalSplitter splitter = getLocalSplitter();
995+
while (it.hasNext()) {
996+
MethodOrMethodContext mc = it.next();
997+
SootMethod m = mc.method();
998+
if (m.isConcrete() && m.getTag(SplittedTag.NAME) == null) {
999+
m.addTag(SplittedTag.v());
1000+
splitter.transform(m.retrieveActiveBody());
1001+
}
1002+
}
1003+
}
1004+
9421005
/**
9431006
* Since these simulations are not perfect, we should remove them afterwards
9441007
*/
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package soot.jimple.infoflow;
2+
3+
import java.util.Map;
4+
5+
import soot.Body;
6+
import soot.Local;
7+
import soot.Singletons.Global;
8+
import soot.jimple.internal.JimpleLocal;
9+
import soot.toolkits.scalar.LocalSplitter;
10+
import soot.toolkits.scalar.UnusedLocalEliminator;
11+
12+
/**
13+
* With more recent soot versions, locals are reused more often. This can cause
14+
* problems in FlowDroid (e.g. the overwriteParameter test case). The simple
15+
* solution: We split these locals beforehand
16+
*
17+
* @author Marc Miltenberger
18+
*/
19+
public class FlowDroidLocalSplitter extends LocalSplitter {
20+
public static class SplittedLocal extends JimpleLocal {
21+
22+
private static final long serialVersionUID = 1L;
23+
private JimpleLocal originalLocal;
24+
25+
public SplittedLocal(JimpleLocal oldLocal) {
26+
super(null, oldLocal.getType());
27+
// do not intern the name again
28+
setName(oldLocal.getName());
29+
if (oldLocal.isUserDefinedLocal()) {
30+
setUserDefinedLocal();
31+
}
32+
33+
this.originalLocal = oldLocal;
34+
while (originalLocal instanceof SplittedLocal) {
35+
originalLocal = ((SplittedLocal) originalLocal).originalLocal;
36+
}
37+
}
38+
39+
public JimpleLocal getOriginalLocal() {
40+
return originalLocal;
41+
}
42+
43+
}
44+
45+
public FlowDroidLocalSplitter() {
46+
super((Global) null);
47+
}
48+
49+
@Override
50+
protected String getNewName(String name, int count) {
51+
// Reuse the old name
52+
return name;
53+
}
54+
55+
@Override
56+
protected Local createClonedLocal(Local oldLocal) {
57+
return new SplittedLocal((JimpleLocal) oldLocal);
58+
}
59+
60+
public static FlowDroidLocalSplitter v() {
61+
return new FlowDroidLocalSplitter();
62+
}
63+
64+
@Override
65+
protected void internalTransform(Body body, String phaseName, Map<String, String> options) {
66+
super.internalTransform(body, phaseName, options);
67+
UnusedLocalEliminator.v().transform(body);
68+
}
69+
70+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package soot.jimple.infoflow;
2+
3+
import soot.tagkit.Tag;
4+
5+
public class SplittedTag implements Tag {
6+
7+
public static final String NAME = "Splitted";
8+
private static final Tag INSTANCE = new SplittedTag();
9+
10+
@Override
11+
public String getName() {
12+
return NAME;
13+
}
14+
15+
public static Tag v() {
16+
return INSTANCE;
17+
}
18+
19+
}

soot-infoflow/src/soot/jimple/infoflow/data/AccessPath.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,4 +586,14 @@ public static AccessPath getZeroAccessPath() {
586586
return zeroAccessPath;
587587
}
588588

589+
/**
590+
* Creates a new access path that is effectively the same, but based on another local as plain value
591+
* @param newValue the new value
592+
* @return the rebased access path
593+
*/
594+
public AccessPath rebaseTo(Local newValue) {
595+
return new AccessPath(newValue, baseType, fragments, taintSubFields, cutOffApproximation, arrayTaintType,
596+
canHaveImmutableAliases);
597+
}
598+
589599
}

soot-infoflow/src/soot/jimple/infoflow/data/pathBuilders/BatchPathBuilder.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public void computeTaintPaths(Set<AbstractionAtSink> res) {
3838
int batchId = 1;
3939
long startTime = System.nanoTime();
4040
long totalTime = manager.getConfig().getPathConfiguration().getPathReconstructionTotalTime();
41+
int completed = 0;
4142

4243
while (resIt.hasNext()) {
4344
// checking if the execution time exceeds the configured totalTime and logging
@@ -86,7 +87,8 @@ public void computeTaintPaths(Set<AbstractionAtSink> res) {
8687
resultExecutor.reset();
8788
}
8889
logger.info("Single batch has used " + (System.nanoTime() - beforeBatch) / 1E9 + " seconds");
89-
90+
completed += batch.size();
91+
reportCompletion(completed, res.size());
9092
// If the analysis failed due to an OOM, it doesn't make sense to proceed with
9193
// the next batch and get into yet another OOM
9294
ISolverTerminationReason currentReason = innerBuilder.getTerminationReason();
@@ -106,6 +108,10 @@ public void computeTaintPaths(Set<AbstractionAtSink> res) {
106108
}
107109
}
108110

111+
protected void reportCompletion(int completed, int totalTasks) {
112+
113+
}
114+
109115
@Override
110116
public InfoflowResults getResults() {
111117
return innerBuilder.getResults();

soot-infoflow/src/soot/jimple/infoflow/results/AbstractResultSourceSinkInfo.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package soot.jimple.infoflow.results;
22

3+
import soot.Local;
34
import soot.jimple.Stmt;
5+
import soot.jimple.infoflow.FlowDroidLocalSplitter.SplittedLocal;
46
import soot.jimple.infoflow.InfoflowConfiguration;
57
import soot.jimple.infoflow.data.AccessPath;
68
import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition;
9+
import soot.jimple.internal.JimpleLocal;
710

811
/**
912
* Abstract base class for information on data flow results
@@ -35,7 +38,7 @@ public AbstractResultSourceSinkInfo(ISourceSinkDefinition definition, AccessPath
3538
assert accessPath != null;
3639

3740
this.definition = definition;
38-
this.accessPath = accessPath;
41+
this.accessPath = replaceAccessPath(accessPath);
3942
this.stmt = stmt;
4043
this.userData = userData;
4144
}
@@ -97,4 +100,15 @@ public boolean equals(Object o) {
97100
return true;
98101
}
99102

103+
protected AccessPath replaceAccessPath(AccessPath accessPath) {
104+
Local pv = accessPath.getPlainValue();
105+
if (pv instanceof SplittedLocal) {
106+
SplittedLocal s = (SplittedLocal) pv;
107+
JimpleLocal orig = s.getOriginalLocal();
108+
109+
AccessPath a = accessPath.rebaseTo(orig);
110+
return a;
111+
}
112+
return accessPath;
113+
}
100114
}

0 commit comments

Comments
 (0)