Skip to content

Commit 782ff5b

Browse files
authored
Merge pull request #623 from timll/develop
StubDroid: do not apply identity on unhandled methods
2 parents 5756eda + 2057f6a commit 782ff5b

File tree

5 files changed

+88
-121
lines changed

5 files changed

+88
-121
lines changed

soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/taintWrappers/SummaryTaintWrapper.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,7 @@ public Set<Abstraction> getTaintsForMethod(Stmt stmt, Abstraction d1, Abstractio
527527
return Collections.singleton(taintedAbs);
528528
else {
529529
reportMissingSummary(callee, stmt, taintedAbs);
530-
if (fallbackWrapper != null) {
531-
return fallbackWrapper.getTaintsForMethod(stmt, d1, taintedAbs);
532-
}
530+
return fallbackWrapper != null ? fallbackWrapper.getTaintsForMethod(stmt, d1, taintedAbs) : null;
533531
}
534532
}
535533
}
@@ -1758,14 +1756,21 @@ public Set<Abstraction> getInverseTaintsForMethod(Stmt stmt, Abstraction d1, Abs
17581756
if (!stmt.containsInvokeExpr())
17591757
return Collections.singleton(taintedAbs);
17601758

1759+
ByReferenceBoolean classSupported = new ByReferenceBoolean(false);
17611760
// Get the cached data flows
17621761
final SootMethod method = stmt.getInvokeExpr().getMethod();
1763-
ClassSummaries flowsInCallees = getFlowSummariesForMethod(stmt, method, taintedAbs, null);
1762+
ClassSummaries flowsInCallees = getFlowSummariesForMethod(stmt, method, taintedAbs, classSupported);
17641763

17651764
// If we have no data flows, we can abort early
1766-
if (flowsInCallees.isEmpty())
1765+
if (flowsInCallees.isEmpty()) {
1766+
if (classSupported.value)
1767+
return Collections.singleton(taintedAbs);
1768+
17671769
if (fallbackWrapper != null && fallbackWrapper instanceof IReversibleTaintWrapper)
17681770
return ((IReversibleTaintWrapper) fallbackWrapper).getInverseTaintsForMethod(stmt, d1, taintedAbs);
1771+
else
1772+
return null;
1773+
}
17691774

17701775
// Create a level-0 propagator for the initially tainted access path
17711776
ByReferenceBoolean killIncomingTaint = new ByReferenceBoolean();

soot-infoflow-summaries/src/soot/jimple/infoflow/methodSummary/taintWrappers/resolvers/SummaryResolver.java

Lines changed: 46 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import soot.jimple.infoflow.methodSummary.data.provider.IMethodSummaryProvider;
1717
import soot.jimple.infoflow.methodSummary.data.summary.ClassMethodSummaries;
1818
import soot.jimple.infoflow.methodSummary.data.summary.ClassSummaries;
19+
import soot.jimple.infoflow.util.ByReferenceBoolean;
1920

2021
/**
2122
* A resolver for finding all applicable summaries for a given method call
@@ -35,32 +36,44 @@ public SummaryResponse load(SummaryQuery query) throws Exception {
3536
final SootClass declaredClass = query.declaredClass;
3637
final String methodSig = query.subsignature;
3738
final ClassSummaries classSummaries = new ClassSummaries();
38-
boolean isClassSupported = false;
39+
boolean directHit = false;
40+
ByReferenceBoolean isClassSupported = new ByReferenceBoolean(false);
3941

4042
// Get the flows in the target method
4143
if (calleeClass != null)
42-
isClassSupported = getSummaries(methodSig, classSummaries, calleeClass);
44+
directHit = getSummaries(methodSig, classSummaries, calleeClass, isClassSupported);
4345

4446
// If we haven't found any summaries, we look at the class from the declared
4547
// type at the call site
46-
if (declaredClass != null && !isClassSupported)
47-
isClassSupported = getSummaries(methodSig, classSummaries, declaredClass);
48+
if (declaredClass != null && !directHit)
49+
directHit = getSummaries(methodSig, classSummaries, declaredClass, isClassSupported);
4850

4951
// If we still don't have anything, we must try the hierarchy. Since this
5052
// best-effort approach can be fairly imprecise, it is our last resort.
51-
if (!isClassSupported && calleeClass != null)
52-
isClassSupported = getSummariesHierarchy(methodSig, classSummaries, calleeClass);
53-
if (declaredClass != null && !isClassSupported)
54-
isClassSupported = getSummariesHierarchy(methodSig, classSummaries, declaredClass);
55-
56-
if (isClassSupported) {
57-
if (classSummaries.isEmpty())
58-
return SummaryResponse.EMPTY_BUT_SUPPORTED;
59-
return new SummaryResponse(classSummaries, isClassSupported);
60-
} else
53+
if (!directHit && calleeClass != null)
54+
directHit = getSummariesHierarchy(methodSig, classSummaries, calleeClass, isClassSupported);
55+
if (declaredClass != null && !directHit)
56+
directHit = getSummariesHierarchy(methodSig, classSummaries, declaredClass, isClassSupported);
57+
58+
if (directHit && !classSummaries.isEmpty())
59+
return new SummaryResponse(classSummaries, true);
60+
else if (directHit || isClassSupported.value)
61+
return SummaryResponse.EMPTY_BUT_SUPPORTED;
62+
else
6163
return SummaryResponse.NOT_SUPPORTED;
6264
}
6365

66+
private void updateClassExclusive(ByReferenceBoolean classSupported, SootClass sc, String subsig) {
67+
if (classSupported.value)
68+
return;
69+
70+
if (sc.getMethodUnsafe(subsig) == null)
71+
return;
72+
73+
ClassMethodSummaries cms = flows.getClassFlows(sc.getName());
74+
classSupported.value |= cms != null && cms.isExclusiveForClass();
75+
}
76+
6477
/**
6578
* Checks whether we have summaries for the given method signature in the given
6679
* class
@@ -70,47 +83,33 @@ public SummaryResponse load(SummaryQuery query) throws Exception {
7083
* @param clazz The class for which to look for summaries
7184
* @return True if summaries were found, false otherwise
7285
*/
73-
private boolean getSummaries(final String methodSig, final ClassSummaries summaries, SootClass clazz) {
86+
private boolean getSummaries(final String methodSig, final ClassSummaries summaries, SootClass clazz,
87+
ByReferenceBoolean classSupported) {
7488
// Do we have direct support for the target class?
7589
if (summaries.merge(flows.getMethodFlows(clazz, methodSig)))
7690
return true;
7791

7892
// Do we support any interface this class might have?
79-
if (checkInterfaces(methodSig, summaries, clazz))
93+
if (checkInterfaces(methodSig, summaries, clazz, classSupported))
8094
return true;
8195

96+
updateClassExclusive(classSupported, clazz, methodSig);
97+
98+
SootMethod targetMethod = clazz.getMethodUnsafe(methodSig);
8299
// If the target is abstract and we haven't found any flows,
83100
// we check for child classes
84-
SootMethod targetMethod = clazz.getMethodUnsafe(methodSig);
85101
if (!clazz.isConcrete() || targetMethod == null || !targetMethod.isConcrete()) {
86102
for (SootClass parentClass : getAllParentClasses(clazz)) {
87103
// Do we have support for the target class?
88104
if (summaries.merge(flows.getMethodFlows(parentClass, methodSig)))
89105
return true;
90106

91107
// Do we support any interface this class might have?
92-
if (checkInterfaces(methodSig, summaries, parentClass))
108+
if (checkInterfaces(methodSig, summaries, parentClass, classSupported))
93109
return true;
94-
}
95-
}
96110

97-
// In case we don't have a real hierarchy, we must reconstruct one from the
98-
// summaries
99-
String curClass = clazz.getName();
100-
while (curClass != null) {
101-
ClassMethodSummaries classSummaries = flows.getClassFlows(curClass);
102-
if (classSummaries != null) {
103-
// Check for flows in the current class
104-
if (summaries.merge(flows.getMethodFlows(curClass, methodSig)))
105-
return true;
106-
107-
// Check for interfaces
108-
if (checkInterfacesFromSummary(methodSig, summaries, curClass))
109-
return true;
110-
111-
curClass = classSummaries.getSuperClass();
112-
} else
113-
break;
111+
updateClassExclusive(classSupported, parentClass, methodSig);
112+
}
114113
}
115114

116115
return false;
@@ -126,7 +125,7 @@ private boolean getSummaries(final String methodSig, final ClassSummaries summar
126125
* @return True if summaries were found, false otherwise
127126
*/
128127
private boolean getSummariesHierarchy(final String methodSig, final ClassSummaries summaries,
129-
SootClass clazz) {
128+
SootClass clazz, ByReferenceBoolean classSupported) {
130129
// Don't try to look up the whole Java hierarchy
131130
if (clazz.getName().equals("java.lang.Object"))
132131
return false;
@@ -145,9 +144,11 @@ private boolean getSummariesHierarchy(final String methodSig, final ClassSummari
145144
found++;
146145

147146
// Do we support any interface this class might have?
148-
if (checkInterfaces(methodSig, summaries, childClass))
147+
if (checkInterfaces(methodSig, summaries, childClass, classSupported))
149148
found++;
150149

150+
updateClassExclusive(classSupported, childClass, methodSig);
151+
151152
// If we have too many summaries that could be applicable, we abort here to
152153
// avoid false positives
153154
if (found > MAX_HIERARCHY_DEPTH)
@@ -167,7 +168,8 @@ private boolean getSummariesHierarchy(final String methodSig, final ClassSummari
167168
* @param clazz The interface for which to look for summaries
168169
* @return True if summaries were found, false otherwise
169170
*/
170-
private boolean checkInterfaces(String methodSig, ClassSummaries summaries, SootClass clazz) {
171+
private boolean checkInterfaces(String methodSig, ClassSummaries summaries, SootClass clazz,
172+
ByReferenceBoolean classSupported) {
171173
for (SootClass intf : clazz.getInterfaces()) {
172174
// Directly check the interface
173175
if (summaries.merge(flows.getMethodFlows(intf, methodSig)))
@@ -177,42 +179,13 @@ private boolean checkInterfaces(String methodSig, ClassSummaries summaries, Soot
177179
// Do we have support for the interface?
178180
if (summaries.merge(flows.getMethodFlows(parent, methodSig)))
179181
return true;
180-
}
181-
}
182182

183-
// We might not have hierarchy information in the scene, so let's check the
184-
// summary itself
185-
return checkInterfacesFromSummary(methodSig, summaries, clazz.getName());
186-
}
187-
188-
/**
189-
* Checks for summaries on the interfaces implemented by the given class. This
190-
* method relies on the hierarchy data from the summary XML files, rather than
191-
* the Soot scene
192-
*
193-
* @param methodSig The subsignature of the method for which to get summaries
194-
* @param summaries The summary object to which to add the discovered summaries
195-
* @param className The interface for which to look for summaries
196-
* @return True if summaries were found, false otherwise
197-
*/
198-
protected boolean checkInterfacesFromSummary(String methodSig, ClassSummaries summaries,
199-
String className) {
200-
List<String> interfaces = new ArrayList<>();
201-
interfaces.add(className);
202-
while (!interfaces.isEmpty()) {
203-
final String intfName = interfaces.remove(0);
204-
ClassMethodSummaries classSummaries = flows.getClassFlows(intfName);
205-
if (classSummaries != null && classSummaries.hasInterfaces()) {
206-
for (String intf : classSummaries.getInterfaces()) {
207-
// Do we have a summary on the current interface?
208-
if (summaries.merge(flows.getMethodFlows(intf, methodSig)))
209-
return true;
210-
211-
// Recursively check for more interfaces
212-
interfaces.add(intf);
213-
}
183+
updateClassExclusive(classSupported, parent, methodSig);
214184
}
215185
}
186+
187+
// We inject the hierarchy from summaries before the data flow analysis, thus the
188+
// soot hierarchy already contains the manual information provided in the xmls.
216189
return false;
217190
}
218191

@@ -246,7 +219,6 @@ private Set<SootClass> getAllChildClasses(SootClass sc) {
246219
Set<SootClass> doneSet = new HashSet<SootClass>();
247220
Set<SootClass> classes = new HashSet<>();
248221

249-
final Scene scene = Scene.v();
250222
while (!workList.isEmpty()) {
251223
SootClass curClass = workList.remove(0);
252224
if (!doneSet.add(curClass))
@@ -255,36 +227,13 @@ private Set<SootClass> getAllChildClasses(SootClass sc) {
255227
if (curClass.isInterface()) {
256228
List<SootClass> hierarchyImplementers = hierarchy.getImplementersOf(curClass);
257229
workList.addAll(hierarchyImplementers);
258-
if (curClass.isPhantom() && hierarchyImplementers.isEmpty()) {
259-
// Query the hierarchy data in the summaries
260-
flows.getImplementersOfInterface(curClass.getName()).stream().map(c -> scene.getSootClassUnsafe(c))
261-
.filter(c -> c != null).forEach(c -> {
262-
workList.add(c);
263-
});
264-
}
265230

266231
List<SootClass> subinterfaces = hierarchy.getSubinterfacesOf(curClass);
267232
workList.addAll(subinterfaces);
268-
if (curClass.isPhantom() && subinterfaces.isEmpty()) {
269-
// Query the hierarchy data in the summaries
270-
flows.getSubInterfacesOf(curClass.getName()).stream().map(c -> scene.getSootClassUnsafe(c))
271-
.filter(c -> c != null).forEach(c -> {
272-
workList.add(c);
273-
});
274-
}
275233
} else {
276234
List<SootClass> hierarchyClasses = hierarchy.getSubclassesOf(curClass);
277235
workList.addAll(hierarchyClasses);
278236
classes.add(curClass);
279-
280-
if (curClass.isPhantom() && hierarchyClasses.isEmpty()) {
281-
// Query the hierarchy data in the summaries
282-
flows.getSubclassesOf(curClass.getName()).stream().map(c -> scene.getSootClassUnsafe(c))
283-
.filter(c -> c != null).forEach(c -> {
284-
workList.add(c);
285-
classes.add(c);
286-
});
287-
}
288237
}
289238
}
290239

@@ -305,7 +254,6 @@ private Set<SootClass> getAllParentClasses(SootClass sc) {
305254
Set<SootClass> doneSet = new HashSet<SootClass>();
306255
Set<SootClass> classes = new HashSet<>();
307256

308-
final Scene scene = Scene.v();
309257
while (!workList.isEmpty()) {
310258
SootClass curClass = workList.remove(0);
311259
if (!doneSet.add(curClass))
@@ -314,27 +262,10 @@ private Set<SootClass> getAllParentClasses(SootClass sc) {
314262
if (curClass.isInterface()) {
315263
List<SootClass> hierarchyClasses = hierarchy.getSuperinterfacesOf(curClass);
316264
workList.addAll(hierarchyClasses);
317-
318-
if (curClass.isPhantom() && hierarchyClasses.isEmpty()) {
319-
// Query the hierarchy data in the summaries
320-
flows.getSuperinterfacesOf(curClass.getName()).stream().map(c -> scene.getSootClassUnsafe(c))
321-
.filter(c -> c != null).forEach(c -> {
322-
workList.add(c);
323-
});
324-
}
325265
} else {
326266
List<SootClass> hierarchyClasses = hierarchy.getSuperclassesOf(curClass);
327267
workList.addAll(hierarchyClasses);
328268
classes.add(curClass);
329-
330-
if (curClass.isPhantom() && hierarchyClasses.isEmpty()) {
331-
// Query the hierarchy data in the summaries
332-
flows.getSuperclassesOf(curClass.getName()).stream().map(c -> scene.getSootClassUnsafe(c))
333-
.filter(c -> c != null).forEach(c -> {
334-
workList.add(c);
335-
classes.add(c);
336-
});
337-
}
338269
}
339270
}
340271

soot-infoflow-summaries/test/soot/jimple/infoflow/test/methodSummary/ApiClassClient.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,22 @@ public void iterator() {
309309
if (it.hasNext())
310310
sink(it.next());
311311
}
312+
313+
private static void overwrite(Data d) {
314+
d.stringField = "";
315+
}
316+
317+
public void noPropagationOverUnhandledCallee() {
318+
Data d = new Data();
319+
d.stringField = stringSource();
320+
overwrite(d);
321+
sink(d.stringField);
322+
}
323+
324+
public void identityIsStillAppliedOnUnhandledMethodButExclusiveClass() {
325+
Data d = new Data();
326+
d.stringField = stringSource();
327+
d.identity();
328+
sink(d.stringField);
329+
}
312330
}

soot-infoflow-summaries/test/soot/jimple/infoflow/test/methodSummary/Data.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,8 @@ public void setI(String i) {
5252
this.stringField = i;
5353
}
5454

55-
55+
public void identity() {
56+
// NO-OP but do something to make sure that this won't get removed by optimizations
57+
System.out.println("Hello World");
58+
}
5659
}

soot-infoflow-summaries/test/soot/jimple/infoflow/test/methodSummary/junit/SummaryTaintWrapperTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,16 @@ public void iterator() {
232232
testFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void iterator()>");
233233
}
234234

235+
@Test(timeout = 30000)
236+
public void noPropagationOverUnhandledCallee() {
237+
testNoFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void noPropagationOverUnhandledCallee()>");
238+
}
239+
240+
@Test(timeout = 30000)
241+
public void identityIsStillAppliedOnUnhandledMethodButExclusiveClass() {
242+
testFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void identityIsStillAppliedOnUnhandledMethodButExclusiveClass()>");
243+
}
244+
235245
@Test
236246
public void testAllSummaries() throws URISyntaxException, IOException {
237247
EagerSummaryProvider provider = new EagerSummaryProvider(TaintWrapperFactory.DEFAULT_SUMMARY_DIR);

0 commit comments

Comments
 (0)