Skip to content

Commit cf4f55d

Browse files
authored
Merge pull request github#5223 from smowton/smowton/feature/backward-dataflow-for-modelled-fluent-methods
Java: Add backward dataflow edges through modelled function invocations
2 parents 32f1da7 + 012058a commit cf4f55d

File tree

4 files changed

+90
-22
lines changed

4 files changed

+90
-22
lines changed

java/ql/src/semmle/code/java/dataflow/FlowSteps.qll

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,57 @@ private module Frameworks {
2020
private import semmle.code.java.frameworks.ApacheHttp
2121
}
2222

23+
/**
24+
* A method that returns the exact value of one of its parameters or the qualifier.
25+
*
26+
* Extend this class and override `returnsValue` to add additional value-preserving steps through a
27+
* method that should be added to the basic local flow step relation.
28+
*
29+
* These steps will be visible for all global data-flow purposes, as well as via
30+
* `DataFlow::Node.getASuccessor` and other related functions exposing intraprocedural dataflow.
31+
*/
32+
abstract class ValuePreservingMethod extends Method {
33+
/**
34+
* Holds if this method returns precisely the value passed into argument `arg`.
35+
* `arg` is a parameter index, or is -1 to indicate the qualifier.
36+
*/
37+
abstract predicate returnsValue(int arg);
38+
}
39+
40+
/**
41+
* A method that returns the exact value of its qualifier (e.g., `return this;`)
42+
*
43+
* Extend this class to add additional value-preserving steps from qualifier to return value through a
44+
* method that should be added to the basic local flow step relation.
45+
*
46+
* These steps will be visible for all global data-flow purposes, as well as via
47+
* `DataFlow::Node.getASuccessor` and other related functions exposing intraprocedural dataflow.
48+
*/
49+
abstract class FluentMethod extends ValuePreservingMethod {
50+
override predicate returnsValue(int arg) { arg = -1 }
51+
}
52+
53+
private class StandardLibraryValuePreservingMethod extends ValuePreservingMethod {
54+
int returnsArgNo;
55+
56+
StandardLibraryValuePreservingMethod() {
57+
this.getDeclaringType().hasQualifiedName("java.util", "Objects") and
58+
(
59+
this.hasName(["requireNonNull", "requireNonNullElseGet"]) and returnsArgNo = 0
60+
or
61+
this.hasName("requireNonNullElse") and returnsArgNo = [0 .. this.getNumberOfParameters() - 1]
62+
or
63+
this.hasName("toString") and returnsArgNo = 1
64+
)
65+
or
66+
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("java.util", "Stack") and
67+
this.hasName("push") and
68+
returnsArgNo = 0
69+
}
70+
71+
override predicate returnsValue(int argNo) { argNo = returnsArgNo }
72+
}
73+
2374
/**
2475
* A unit class for adding additional taint steps.
2576
*

java/ql/src/semmle/code/java/dataflow/internal/DataFlowUtil.qll

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ private import semmle.code.java.dataflow.SSA
88
private import semmle.code.java.dataflow.TypeFlow
99
private import semmle.code.java.controlflow.Guards
1010
private import semmle.code.java.dataflow.ExternalFlow
11+
private import semmle.code.java.dataflow.FlowSteps
1112
import semmle.code.java.dataflow.InstanceAccess
1213

1314
cached
@@ -408,28 +409,11 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
408409
or
409410
summaryStep(node1, node2, "value")
410411
or
411-
exists(MethodAccess ma, Method m |
412-
ma = node2.asExpr() and
413-
m = ma.getMethod() and
414-
m.getDeclaringType().hasQualifiedName("java.util", "Objects") and
415-
(
416-
m.hasName(["requireNonNull", "requireNonNullElseGet"]) and node1.asExpr() = ma.getArgument(0)
417-
or
418-
m.hasName("requireNonNullElse") and node1.asExpr() = ma.getAnArgument()
419-
or
420-
m.hasName("toString") and node1.asExpr() = ma.getArgument(1)
421-
)
422-
)
423-
or
424-
exists(MethodAccess ma, Method m |
425-
ma = node2.asExpr() and
426-
m = ma.getMethod() and
427-
m.getDeclaringType()
428-
.getSourceDeclaration()
429-
.getASourceSupertype*()
430-
.hasQualifiedName("java.util", "Stack") and
431-
m.hasName("push") and
432-
node1.asExpr() = ma.getArgument(0)
412+
exists(MethodAccess ma, ValuePreservingMethod m, int argNo |
413+
ma.getCallee().getSourceDeclaration() = m and m.returnsValue(argNo)
414+
|
415+
node2.asExpr() = ma and
416+
node1.(ArgumentNode).argumentOf(ma, argNo)
433417
)
434418
}
435419

java/ql/test/library-tests/dataflow/fluent-methods/Test.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ public Test fluentNoop() {
66
return this;
77
}
88

9+
public Test modelledFluentMethod() {
10+
// A model in the accompanying .ql file will indicate that the qualifier flows to the return value.
11+
return null;
12+
}
13+
14+
public static Test modelledIdentity(Test t) {
15+
// A model in the accompanying .ql file will indicate that the argument flows to the return value.
16+
return null;
17+
}
18+
919
public Test indirectlyFluentNoop() {
1020
return this.fluentNoop();
1121
}
@@ -47,4 +57,16 @@ public static void test3() {
4757
sink(t.get()); // $hasTaintFlow=y
4858
}
4959

60+
public static void testModel1() {
61+
Test t = new Test();
62+
t.indirectlyFluentNoop().modelledFluentMethod().fluentSet(source()).fluentNoop();
63+
sink(t.get()); // $hasTaintFlow=y
64+
}
65+
66+
public static void testModel2() {
67+
Test t = new Test();
68+
Test.modelledIdentity(t).indirectlyFluentNoop().modelledFluentMethod().fluentSet(source()).fluentNoop();
69+
sink(t.get()); // $hasTaintFlow=y
70+
}
71+
5072
}

java/ql/test/library-tests/dataflow/fluent-methods/flow.ql

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import java
22
import semmle.code.java.dataflow.DataFlow
3+
import semmle.code.java.dataflow.FlowSteps
34
import TestUtilities.InlineExpectationsTest
45

56
class Conf extends DataFlow::Configuration {
@@ -14,6 +15,16 @@ class Conf extends DataFlow::Configuration {
1415
}
1516
}
1617

18+
class Model extends FluentMethod {
19+
Model() { this.getName() = "modelledFluentMethod" }
20+
}
21+
22+
class IdentityModel extends ValuePreservingMethod {
23+
IdentityModel() { this.getName() = "modelledIdentity" }
24+
25+
override predicate returnsValue(int arg) { arg = 0 }
26+
}
27+
1728
class HasFlowTest extends InlineExpectationsTest {
1829
HasFlowTest() { this = "HasFlowTest" }
1930

0 commit comments

Comments
 (0)