Skip to content

Commit 61f1756

Browse files
committed
Merge branch 'develop' of https://github.com/secure-software-engineering/FlowDroid into dev-marc
2 parents 36117e6 + 735737a commit 61f1756

File tree

10 files changed

+125
-94
lines changed

10 files changed

+125
-94
lines changed

soot-infoflow-integration/test/soot/jimple/infoflow/integration/test/junit/river/BaseJUnitTests.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import soot.jimple.infoflow.sourcesSinks.definitions.ISourceSinkDefinition;
1717
import soot.jimple.infoflow.sourcesSinks.manager.BaseSourceSinkManager;
1818
import soot.jimple.infoflow.taintWrappers.ITaintPropagationWrapper;
19+
import soot.jimple.infoflow.test.base.AbstractJUnitTests;
1920
import soot.jimple.infoflow.test.junit.JUnitTests;
2021

2122
/**
@@ -111,13 +112,8 @@ protected SetupApplication initApplication(File apkFile) {
111112
*
112113
* @return The directory in which the FlowDroid main project is located
113114
*/
114-
public static File getIntegrationRoot() {
115-
File testRoot = new File(".");
116-
if (!new File(testRoot, "testAPKs").exists())
117-
testRoot = new File(testRoot, "soot-infoflow-integration");
118-
if (!new File(testRoot, "testAPKs").exists())
119-
throw new RuntimeException(String.format("Test root not found in %s", testRoot.getAbsolutePath()));
120-
return testRoot;
115+
public static File getIntegrationRoot() throws IOException {
116+
return AbstractJUnitTests.getInfoflowRoot(BaseJUnitTests.class, "soot-infoflow-integration");
121117
}
122118

123119
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1720,7 +1720,7 @@ protected Taint addSinkTaint(MethodFlow flow, Taint taint, GapDefinition gap, St
17201720
String sBaseType = sinkType == null ? null : "" + sinkType;
17211721
if (!flow.getIgnoreTypes()) {
17221722
// Compute the new base type
1723-
Type newBaseType = manager.getTypeUtils().getMorePreciseType(taintType, sinkType);
1723+
Type newBaseType = manager.getTypeUtils().getMorePreciseType(sinkType, taintType);
17241724
if (newBaseType == null)
17251725
newBaseType = sinkType;
17261726

soot-infoflow-summaries/summariesManual/java.util.Collection.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
ImplicitLocation="Next" />
1111
</constraints>
1212
<flows>
13-
<flow isAlias="true" typeChecking="false" final="true">
13+
<flow isAlias="withContext" typeChecking="false" final="true">
1414
<from sourceSinkType="Parameter" ParameterIndex="0" />
1515
<to sourceSinkType="Field"
1616
AccessPath="[java.util.Collection: java.lang.Object[] innerArray]"

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,16 +585,23 @@ public void stringConcatTest() {
585585
}
586586

587587
public void listIrrelevantItemTest() {
588-
String secret = TelephonyManager.getDeviceId();
588+
String secret = stringSource();
589589

590590
List<Object> lvar = new ArrayList<>();
591591
Boolean bvar = true;
592592

593593
lvar.add(secret); // Adds tainted data to the list
594594
lvar.add(bvar);
595595

596-
ConnectionManager cm = new ConnectionManager();
597-
cm.publish(bvar);
596+
sink(bvar);
598597
}
599598

599+
public void testTypeNarrowing() {
600+
String secret = stringSource();
601+
// split: String -> String[]
602+
// Tests that getMorePreciseType correctly
603+
// keeps the array in the type
604+
Object[] splitted = secret.split(";");
605+
sink(splitted);
606+
}
600607
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ protected void testFlowForMethod(String m) {
5151
}
5252

5353
protected void testFlowForMethod(String m, int count) {
54-
54+
testFlowForMethod(m, count, null);
5555
}
5656

5757
protected void testFlowForMethod(String m, int count, Consumer<InfoflowConfiguration> configCallback) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import soot.jimple.infoflow.config.ConfigForTest;
2929
import soot.jimple.infoflow.results.InfoflowResults;
3030
import soot.jimple.infoflow.taintWrappers.EasyTaintWrapper;
31+
import soot.jimple.infoflow.test.base.AbstractJUnitTests;
3132

3233
/**
3334
* abstract super class of all test cases which handles initialization, keeps
@@ -58,7 +59,7 @@ public abstract class JUnitTests {
5859

5960
@BeforeClass
6061
public static void setUp() throws IOException {
61-
File f = new File(".");
62+
File f = AbstractJUnitTests.getInfoflowRoot(JUnitTests.class, "soot-infoflow-summaries");
6263
File testSrc1 = new File(f, "bin");
6364
File testSrc4 = new File(f, "testBin");
6465
File testSrc2 = new File(f, "build" + File.separator + "classes");

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,12 @@ public void stringConcatTest() {
338338

339339
@Test(timeout = 30000)
340340
public void listIrrelevantItemTest() {
341-
testFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void listIrrelevantItemTest()>", 1);
341+
testNoFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void listIrrelevantItemTest()>");
342+
}
343+
344+
@Test(timeout = 30000)
345+
public void testTypeNarrowing() {
346+
testFlowForMethod("<soot.jimple.infoflow.test.methodSummary.ApiClassClient: void testTypeNarrowing()>", 1);
342347
}
343348

344349
@Test

soot-infoflow/src/soot/jimple/infoflow/typing/TypeUtils.java

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -191,66 +191,67 @@ public boolean hasCompatibleTypesForCall(AccessPath apBase, SootClass dest) {
191191
/**
192192
* Gets the more precise one of the two given types. If there is no ordering
193193
* (i.e., the two types are not cast-compatible) null is returned.
194-
*
195-
* @param tp1 The first type
196-
* @param tp2 The second type
194+
* IMPORTANT: this method is not commutative on array types. The second type must
195+
* always be the declared type, which is used to infer the array depth.
196+
* Consider, for example, the case
197+
* objArr[i] = str;
198+
* where we can narrow the type of objArr to String[]. Vice versa,
199+
* obj = strArr[i];
200+
* allows us to narrow the type of obj to String. Therefore,
201+
* getMorePreciseType(String, Object[]) should return String[] and
202+
* getMorePreciseType(Object[], String) should return String.
203+
*
204+
* @param possibleRefinement The first type
205+
* @param declType The second type
197206
* @return The more precise one of the two given types
198207
*/
199-
public Type getMorePreciseType(Type tp1, Type tp2) {
208+
public Type getMorePreciseType(Type possibleRefinement, Type declType) {
200209
final FastHierarchy fastHierarchy = scene.getOrMakeFastHierarchy();
201210

202-
if (tp1 == null)
203-
return tp2;
204-
else if (tp2 == null)
205-
return tp1;
206-
else if (tp1 == tp2)
207-
return tp1;
208-
else if (TypeUtils.isObjectLikeType(tp1))
209-
return tp2;
210-
else if (TypeUtils.isObjectLikeType(tp2))
211-
return tp1;
212-
else if (tp1 instanceof PrimType && tp2 instanceof PrimType)
211+
if (declType == null)
212+
return possibleRefinement;
213+
else if (possibleRefinement == null)
214+
return declType;
215+
else if (declType == possibleRefinement)
216+
return declType;
217+
else if (TypeUtils.isObjectLikeType(declType))
218+
return possibleRefinement;
219+
else if (TypeUtils.isObjectLikeType(possibleRefinement))
220+
return declType;
221+
else if (declType instanceof PrimType && possibleRefinement instanceof PrimType)
213222
return null;
214-
else if (fastHierarchy.canStoreType(tp2, tp1))
215-
return tp2;
216-
else if (fastHierarchy.canStoreType(tp1, tp2))
217-
return tp1;
223+
else if (fastHierarchy.canStoreType(possibleRefinement, declType))
224+
return possibleRefinement;
225+
else if (fastHierarchy.canStoreType(declType, possibleRefinement))
226+
return declType;
218227
else {
219228
// If one type is an array type and the other one is the base type,
220229
// we still accept the cast
221-
if (tp1 instanceof ArrayType && tp2 instanceof ArrayType) {
222-
ArrayType at1 = (ArrayType) tp1;
223-
ArrayType at2 = (ArrayType) tp2;
230+
if (declType instanceof ArrayType && possibleRefinement instanceof ArrayType) {
231+
ArrayType at1 = (ArrayType) possibleRefinement;
232+
ArrayType at2 = (ArrayType) declType;
224233
if (at1.numDimensions != at2.numDimensions)
225234
return null;
226235
Type preciseType = getMorePreciseType(at1.getElementType(), at2.getElementType());
227236
if (preciseType == null)
228237
return null;
229238

230-
return ArrayType.v(preciseType, at1.numDimensions);
231-
} else if (tp1 instanceof ArrayType) {
232-
ArrayType at = (ArrayType) tp1;
233-
return getMorePreciseType(at.getElementType(), tp2);
234-
} else if (tp2 instanceof ArrayType) {
235-
ArrayType at = (ArrayType) tp2;
236-
return getMorePreciseType(tp1, at.getElementType());
239+
return ArrayType.v(preciseType, at2.numDimensions);
240+
} else if (declType instanceof ArrayType) {
241+
ArrayType at = (ArrayType) declType;
242+
Type preciseType = getMorePreciseType(possibleRefinement, at.getElementType());
243+
if (preciseType == null)
244+
return null;
245+
246+
return ArrayType.v(preciseType, at.numDimensions);
247+
} else if (possibleRefinement instanceof ArrayType) {
248+
ArrayType at = (ArrayType) possibleRefinement;
249+
return getMorePreciseType(at.getElementType(), declType);
237250
}
238251
}
239252
return null;
240253
}
241254

242-
/**
243-
* Gets the more precise one of the two given types
244-
*
245-
* @param tp1 The first type
246-
* @param tp2 The second type
247-
* @return The more precise one of the two given types
248-
*/
249-
public String getMorePreciseType(String tp1, String tp2) {
250-
Type newType = getMorePreciseType(getTypeFromString(tp1), getTypeFromString(tp2));
251-
return newType == null ? null : "" + newType;
252-
}
253-
254255
/**
255256
* Creates a Soot Type from the given string
256257
*

soot-infoflow/test/soot/jimple/infoflow/test/base/AbstractJUnitTests.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import java.io.File;
1717
import java.io.IOException;
1818

19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
1921
import org.springframework.web.multipart.MultipartHttpServletRequest;
2022

2123
import jakarta.servlet.http.HttpServlet;
@@ -28,6 +30,8 @@
2830
*/
2931
public abstract class AbstractJUnitTests {
3032

33+
protected static final Logger logger = LoggerFactory.getLogger(AbstractJUnitTests.class);
34+
3135
/**
3236
* Appends the given path to the given {@link StringBuilder} if it exists
3337
*
@@ -89,4 +93,57 @@ protected static void addRtJarPath(StringBuilder libPathBuilder) throws IOExcept
8993
appendWithSeparator(libPathBuilder, new File(springJAR));
9094
}
9195

96+
/**
97+
* Gets the root of the current project from a reference class located in that
98+
* project
99+
*
100+
* @param referenceClass The reference class
101+
* @param moduleName The name of the FlowDroid module for which to get the
102+
* root folder
103+
* @return The root folder of the project
104+
* @throws IOException
105+
*/
106+
public static File getInfoflowRoot(Class<?> referenceClass, String moduleName) throws IOException {
107+
File classFile = new File(referenceClass.getProtectionDomain().getCodeSource().getLocation().getPath());
108+
File f = classFile;
109+
if (f.exists()) {
110+
while (!f.getName().equals(moduleName) && f.getParentFile() != null)
111+
f = f.getParentFile();
112+
113+
// The project root must exist and must not be the file system root
114+
if (f.exists() && f.getParentFile() != null)
115+
return f;
116+
117+
logger.warn("Finding project root from class file {} failed", classFile);
118+
} else
119+
logger.warn("Class file {} does not exist", classFile);
120+
return getInfoflowRoot(moduleName);
121+
}
122+
123+
/**
124+
* Gets the root in which the FlowDroid main project is located
125+
*
126+
* @param moduleName The name of the FlowDroid module for which to get the root
127+
* folder
128+
* @return The directory in which the FlowDroid main project is located
129+
*/
130+
public static File getInfoflowRoot(String moduleName) throws IOException {
131+
File testRoot = new File(".").getCanonicalFile();
132+
133+
if (!new File(testRoot, "src").exists()) {
134+
// Try a subfolder
135+
File subFolder = new File(testRoot, moduleName);
136+
if (subFolder.exists())
137+
testRoot = subFolder;
138+
else {
139+
// Try a sibling folder
140+
testRoot = new File(testRoot.getParentFile(), moduleName);
141+
}
142+
}
143+
144+
if (!new File(testRoot, "src").exists())
145+
throw new RuntimeException(String.format("Test root not found in %s", testRoot.getAbsolutePath()));
146+
return testRoot;
147+
}
148+
92149
}

soot-infoflow/test/soot/jimple/infoflow/test/junit/JUnitTests.java

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,10 @@ public static void setUp() throws IOException {
7474
if (!fi.getCanonicalFile().equals(f.getCanonicalFile())) {
7575
addTestPathes(fi, appPathBuilder);
7676
}
77-
fi = new File(f, "../soot-infoflow-summaries");
78-
if (!fi.getCanonicalFile().equals(f.getCanonicalFile())) {
79-
addTestPathes(fi, appPathBuilder);
80-
}
8177
fi = new File("soot-infoflow");
8278
if (fi.exists()) {
8379
addTestPathes(fi, appPathBuilder);
8480
}
85-
fi = new File("soot-infoflow-summaries");
86-
if (fi.exists()) {
87-
addTestPathes(fi, appPathBuilder);
88-
}
8981
appPath = appPathBuilder.toString();
9082

9183
StringBuilder libPathBuilder = new StringBuilder();
@@ -210,20 +202,7 @@ protected void onlyForwards(IInfoflow infoflow, String message) {
210202
* @throws IOException
211203
*/
212204
public static File getInfoflowRoot(Class<?> referenceClass) throws IOException {
213-
File classFile = new File(referenceClass.getProtectionDomain().getCodeSource().getLocation().getPath());
214-
File f = classFile;
215-
if (f.exists()) {
216-
while (!f.getName().equals("soot-infoflow") && f.getParentFile() != null)
217-
f = f.getParentFile();
218-
219-
// The project root must exist and must not be the file system root
220-
if (f.exists() && f.getParentFile() != null)
221-
return f;
222-
223-
logger.warn("Finding project root from class file {} failed", classFile);
224-
} else
225-
logger.warn("Class file {} does not exist", classFile);
226-
return getInfoflowRoot();
205+
return getInfoflowRoot(referenceClass, "soot-infoflow");
227206
}
228207

229208
/**
@@ -232,22 +211,7 @@ public static File getInfoflowRoot(Class<?> referenceClass) throws IOException {
232211
* @return The directory in which the FlowDroid main project is located
233212
*/
234213
public static File getInfoflowRoot() throws IOException {
235-
File testRoot = new File(".").getCanonicalFile();
236-
237-
if (!new File(testRoot, "src").exists()) {
238-
// Try a subfolder
239-
File subFolder = new File(testRoot, "soot-infoflow");
240-
if (subFolder.exists())
241-
testRoot = subFolder;
242-
else {
243-
// Try a sibling folder
244-
testRoot = new File(testRoot.getParentFile(), "soot-infoflow");
245-
}
246-
}
247-
248-
if (!new File(testRoot, "src").exists())
249-
throw new RuntimeException(String.format("Test root not found in %s", testRoot.getAbsolutePath()));
250-
return testRoot;
214+
return getInfoflowRoot("soot-infoflow");
251215
}
252216

253217
}

0 commit comments

Comments
 (0)