Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fadbb32

Browse files
committed
Add backward dataflow edges through fluent function invocations.
This means that much as obj.getA().setB(...) already has a side-effect on `obj`, all three setters in obj.setA(...).setB(...).setC(...) will have a side-effect on `obj`.
1 parent 37baf77 commit fadbb32

5 files changed

Lines changed: 102 additions & 1 deletion

File tree

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
223223
* Holds if data can flow in one local step from `node1` to `node2`.
224224
*/
225225
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
226-
simpleLocalFlowStep(node1, node2) and
226+
(
227+
simpleLocalFlowStep(node1, node2) or
228+
reverseStepThroughInputOutputAlias(node1, node2)
229+
) and
227230
not outBarrier(node1, config) and
228231
not inBarrier(node2, config) and
229232
not fullBarrier(node1, config) and

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,22 @@ private module Cached {
415415
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
416416
}
417417

418+
/**
419+
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
420+
* nodes of some function output and input respectively, where the output and input
421+
* are aliases. A typical example is a function returning `this`, implementing a fluent
422+
* interface.
423+
*/
424+
cached
425+
predicate reverseStepThroughInputOutputAlias(Node fromNode, Node toNode) {
426+
exists(Node fromPre, Node toPre |
427+
fromPre = fromNode.(PostUpdateNode).getPreUpdateNode() and
428+
toPre = toNode.(PostUpdateNode).getPreUpdateNode()
429+
|
430+
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
431+
)
432+
}
433+
418434
/**
419435
* Holds if the call context `call` either improves virtual dispatch in
420436
* `callable` or if it allows us to prune unreachable nodes in `callable`.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package smowton;
2+
3+
public class Test {
4+
5+
private String field;
6+
7+
public Test fluentNoop() {
8+
return this;
9+
}
10+
11+
public Test indirectlyFluentNoop() {
12+
return this.fluentNoop();
13+
}
14+
15+
public Test fluentSet(String x) {
16+
this.field = x;
17+
return this;
18+
}
19+
20+
public static Test identity(Test t) {
21+
return t;
22+
}
23+
24+
public String get() {
25+
return field;
26+
}
27+
28+
public static String source() {
29+
return "taint";
30+
}
31+
32+
public static void sink(String s) {}
33+
34+
public static void test1() {
35+
Test t = new Test();
36+
t.fluentNoop().fluentSet(source()).fluentNoop();
37+
sink(t.get()); // $hasTaintFlow=y
38+
}
39+
40+
public static void test2() {
41+
Test t = new Test();
42+
Test.identity(t).fluentNoop().fluentSet(source()).fluentNoop();
43+
sink(t.get()); // $hasTaintFlow=y
44+
}
45+
46+
public static void test3() {
47+
Test t = new Test();
48+
t.indirectlyFluentNoop().fluentSet(source()).fluentNoop();
49+
sink(t.get()); // $hasTaintFlow=y
50+
}
51+
52+
}

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

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import java
2+
import semmle.code.java.dataflow.TaintTracking
3+
import TestUtilities.InlineExpectationsTest
4+
5+
class Conf extends TaintTracking::Configuration {
6+
Conf() { this = "qltest:dataflow:fluent-methods" }
7+
8+
override predicate isSource(DataFlow::Node n) {
9+
n.asExpr().(MethodAccess).getMethod().hasName("source")
10+
}
11+
12+
override predicate isSink(DataFlow::Node n) {
13+
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
14+
}
15+
}
16+
17+
class HasFlowTest extends InlineExpectationsTest {
18+
HasFlowTest() { this = "HasFlowTest" }
19+
20+
override string getARelevantTag() { result = "hasTaintFlow" }
21+
22+
override predicate hasActualResult(Location location, string element, string tag, string value) {
23+
tag = "hasTaintFlow" and
24+
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
25+
sink.getLocation() = location and
26+
element = sink.toString() and
27+
value = "y"
28+
)
29+
}
30+
}

0 commit comments

Comments
 (0)