Skip to content

Commit c042c9d

Browse files
committed
Update query to path-query
1 parent a0e61b4 commit c042c9d

File tree

3 files changed

+130
-18
lines changed

3 files changed

+130
-18
lines changed

java/src/security/Recursion/Recursion.ql

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @name Recursive functions
33
* @id tob/java/unbounded-recursion
44
* @description Detects possibly unbounded recursive calls
5-
* @kind problem
5+
* @kind path-problem
66
* @tags security
77
* @precision low
88
* @problem.severity warning
@@ -11,27 +11,57 @@
1111
*/
1212

1313
import java
14+
import semmle.code.java.dataflow.DataFlow
1415

1516
predicate isTestPackage(RefType referenceType) {
1617
referenceType.getPackage().getName().toLowerCase().matches("%test%") or
1718
referenceType.getPackage().getName().toLowerCase().matches("%benchmark%") or
1819
referenceType.getName().toLowerCase().matches("%test%")
1920
}
2021

21-
class RecursiveMethod extends Method {
22-
RecursiveMethod() {
23-
exists(Method m | this.calls+(m) and this = m)
22+
class RecursionSource extends MethodCall {
23+
RecursionSource() { not isTestPackage(this.getCaller().getDeclaringType()) }
24+
25+
override string toString() {
26+
result = this.getCaller().toString() + " calls " + this.getCallee().toString()
2427
}
2528
}
2629

27-
/**
28-
* TODO ideas:
29-
* - limit results to methods that take any user input
30-
* - check if recursive calls are bounded (takes argument that is decremented for every call)
30+
module RecursiveConfig implements DataFlow::StateConfigSig {
31+
class FlowState = Method;
32+
33+
predicate isSource(DataFlow::Node node, FlowState state) {
34+
node.asExpr() instanceof RecursionSource and
35+
state = node.asExpr().(MethodCall).getCaller()
36+
}
37+
38+
predicate isSink(DataFlow::Node node, FlowState state) {
39+
node.asExpr() instanceof RecursionSource and
40+
state.calls+(node.asExpr().(MethodCall).getCaller()) and
41+
node.asExpr().(MethodCall).getCallee().calls(state)
42+
}
43+
44+
predicate isBarrier(DataFlow::Node node) {
45+
node.asExpr() instanceof MethodCall and
46+
exists(Expr arg | arg = node.asExpr().(MethodCall).getAnArgument() |
47+
arg instanceof BinaryExpr or
48+
exists(BinaryExpr b | DataFlow::localFlow(DataFlow::exprNode(b), DataFlow::exprNode(arg)))
49+
)
50+
}
51+
}
52+
53+
module RecursiveFlow = DataFlow::GlobalWithState<RecursiveConfig>;
54+
55+
import RecursiveFlow::PathGraph
56+
57+
/*
58+
* TODO: This query could be improved with the following ideas:
59+
* - limit results to methods that take any user input
3160
* - do not return methods that have calls to self (or unbounded recursion) that are conditional
32-
* - gather and print whole call graph (list of calls from recursiveMethod to itself)
61+
* - gather and print whole call graph (list of calls from recursiveMethod to itself)
3362
*/
34-
from RecursiveMethod recursiveMethod
35-
where
36-
not isTestPackage(recursiveMethod.getDeclaringType())
37-
select recursiveMethod, "Method $@ has unbounded, possibly infinite recursive calls", recursiveMethod, recursiveMethod.toString()
63+
64+
from RecursiveFlow::PathNode source, RecursiveFlow::PathNode sink
65+
where RecursiveFlow::flowPath(source, sink)
66+
// TODO(dm): de-duplicate results
67+
select sink.getNode(), source, sink, "Found a recursion: "
Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,75 @@
1-
| Recursion.java:8:16:8:20 | bar(...) | $@ | Recursion.java:8:16:8:20 | bar(...) | Recursion(2): bar(2) -> foo(7) -> bar |
2-
| Recursion.java:12:16:12:32 | directRecursive(...) | $@ | Recursion.java:12:16:12:32 | directRecursive(...) | Recursion(1): directRecursive(11) -> directRecursive |
3-
| Recursion.java:31:16:31:23 | level0(...) | $@ | Recursion.java:31:16:31:23 | level0(...) | Recursion(3): level0(15) -> level1(21) -> level2(27) -> level0 |
1+
edges
2+
| Recursion.java:53:33:53:55 | readToken calls read : Token | Recursion.java:59:24:59:28 | token : Token | provenance | |
3+
| Recursion.java:57:24:57:34 | readToken calls readToken : Token | Recursion.java:57:24:57:34 | readToken calls readToken | provenance | |
4+
| Recursion.java:57:24:57:34 | readToken calls readToken : Token | Recursion.java:57:24:57:34 | readToken calls readToken : Token | provenance | |
5+
| Recursion.java:59:24:59:28 | token : Token | Recursion.java:57:24:57:34 | readToken calls readToken | provenance | |
6+
| Recursion.java:59:24:59:28 | token : Token | Recursion.java:57:24:57:34 | readToken calls readToken : Token | provenance | |
7+
| Recursion.java:71:29:71:33 | bar calls foo : Boolean | Recursion.java:72:16:72:24 | fooResult : Boolean | provenance | |
8+
| Recursion.java:71:29:71:33 | bar calls foo : Boolean | Recursion.java:72:16:72:24 | fooResult : Boolean | provenance | |
9+
| Recursion.java:72:16:72:24 | fooResult : Boolean | Recursion.java:76:16:76:20 | foo calls bar | provenance | |
10+
| Recursion.java:72:16:72:24 | fooResult : Boolean | Recursion.java:76:16:76:20 | foo calls bar : Boolean | provenance | |
11+
| Recursion.java:72:16:72:24 | fooResult : Boolean | Recursion.java:76:16:76:20 | foo calls bar : Boolean | provenance | |
12+
| Recursion.java:76:16:76:20 | foo calls bar : Boolean | Recursion.java:71:29:71:33 | bar calls foo | provenance | |
13+
| Recursion.java:76:16:76:20 | foo calls bar : Boolean | Recursion.java:71:29:71:33 | bar calls foo : Boolean | provenance | |
14+
| Recursion.java:76:16:76:20 | foo calls bar : Boolean | Recursion.java:71:29:71:33 | bar calls foo : Boolean | provenance | |
15+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive : Boolean | Recursion.java:81:16:81:32 | directRecursive calls directRecursive | provenance | |
16+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive : Boolean | Recursion.java:81:16:81:32 | directRecursive calls directRecursive : Boolean | provenance | |
17+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | Recursion.java:101:16:101:23 | level2 calls level0 | provenance | |
18+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | provenance | |
19+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | provenance | |
20+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | provenance | |
21+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | Recursion.java:89:16:89:23 | level0 calls level1 | provenance | |
22+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | provenance | |
23+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | provenance | |
24+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | provenance | |
25+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | Recursion.java:95:16:95:23 | level1 calls level2 | provenance | |
26+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | provenance | |
27+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | provenance | |
28+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | provenance | |
29+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | provenance | |
30+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean | provenance | |
31+
nodes
32+
| Recursion.java:53:33:53:55 | readToken calls read : Token | semmle.label | readToken calls read : Token |
33+
| Recursion.java:57:24:57:34 | readToken calls readToken | semmle.label | readToken calls readToken |
34+
| Recursion.java:57:24:57:34 | readToken calls readToken : Token | semmle.label | readToken calls readToken : Token |
35+
| Recursion.java:59:24:59:28 | token : Token | semmle.label | token : Token |
36+
| Recursion.java:71:29:71:33 | bar calls foo | semmle.label | bar calls foo |
37+
| Recursion.java:71:29:71:33 | bar calls foo : Boolean | semmle.label | bar calls foo : Boolean |
38+
| Recursion.java:71:29:71:33 | bar calls foo : Boolean | semmle.label | bar calls foo : Boolean |
39+
| Recursion.java:72:16:72:24 | fooResult : Boolean | semmle.label | fooResult : Boolean |
40+
| Recursion.java:72:16:72:24 | fooResult : Boolean | semmle.label | fooResult : Boolean |
41+
| Recursion.java:76:16:76:20 | foo calls bar | semmle.label | foo calls bar |
42+
| Recursion.java:76:16:76:20 | foo calls bar : Boolean | semmle.label | foo calls bar : Boolean |
43+
| Recursion.java:76:16:76:20 | foo calls bar : Boolean | semmle.label | foo calls bar : Boolean |
44+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive | semmle.label | directRecursive calls directRecursive |
45+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive : Boolean | semmle.label | directRecursive calls directRecursive : Boolean |
46+
| Recursion.java:89:16:89:23 | level0 calls level1 | semmle.label | level0 calls level1 |
47+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | semmle.label | level0 calls level1 : Boolean |
48+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | semmle.label | level0 calls level1 : Boolean |
49+
| Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | semmle.label | level0 calls level1 : Boolean |
50+
| Recursion.java:95:16:95:23 | level1 calls level2 | semmle.label | level1 calls level2 |
51+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | semmle.label | level1 calls level2 : Boolean |
52+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | semmle.label | level1 calls level2 : Boolean |
53+
| Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | semmle.label | level1 calls level2 : Boolean |
54+
| Recursion.java:101:16:101:23 | level2 calls level0 | semmle.label | level2 calls level0 |
55+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | semmle.label | level2 calls level0 : Boolean |
56+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | semmle.label | level2 calls level0 : Boolean |
57+
| Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | semmle.label | level2 calls level0 : Boolean |
58+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | semmle.label | directRecursiveNoDepth calls directRecursiveNoDepth |
59+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean | semmle.label | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean |
60+
subpaths
61+
#select
62+
| Recursion.java:57:24:57:34 | readToken calls readToken | Recursion.java:53:33:53:55 | readToken calls read : Token | Recursion.java:57:24:57:34 | readToken calls readToken | Found a recursion: |
63+
| Recursion.java:57:24:57:34 | readToken calls readToken | Recursion.java:57:24:57:34 | readToken calls readToken | Recursion.java:57:24:57:34 | readToken calls readToken | Found a recursion: |
64+
| Recursion.java:57:24:57:34 | readToken calls readToken | Recursion.java:57:24:57:34 | readToken calls readToken : Token | Recursion.java:57:24:57:34 | readToken calls readToken | Found a recursion: |
65+
| Recursion.java:71:29:71:33 | bar calls foo | Recursion.java:71:29:71:33 | bar calls foo | Recursion.java:71:29:71:33 | bar calls foo | Found a recursion: |
66+
| Recursion.java:71:29:71:33 | bar calls foo | Recursion.java:71:29:71:33 | bar calls foo : Boolean | Recursion.java:71:29:71:33 | bar calls foo | Found a recursion: |
67+
| Recursion.java:76:16:76:20 | foo calls bar | Recursion.java:76:16:76:20 | foo calls bar | Recursion.java:76:16:76:20 | foo calls bar | Found a recursion: |
68+
| Recursion.java:76:16:76:20 | foo calls bar | Recursion.java:76:16:76:20 | foo calls bar : Boolean | Recursion.java:76:16:76:20 | foo calls bar | Found a recursion: |
69+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive | Recursion.java:81:16:81:32 | directRecursive calls directRecursive | Recursion.java:81:16:81:32 | directRecursive calls directRecursive | Found a recursion: |
70+
| Recursion.java:81:16:81:32 | directRecursive calls directRecursive | Recursion.java:81:16:81:32 | directRecursive calls directRecursive : Boolean | Recursion.java:81:16:81:32 | directRecursive calls directRecursive | Found a recursion: |
71+
| Recursion.java:89:16:89:23 | level0 calls level1 | Recursion.java:101:16:101:23 | level2 calls level0 : Boolean | Recursion.java:89:16:89:23 | level0 calls level1 | Found a recursion: |
72+
| Recursion.java:95:16:95:23 | level1 calls level2 | Recursion.java:89:16:89:23 | level0 calls level1 : Boolean | Recursion.java:95:16:95:23 | level1 calls level2 | Found a recursion: |
73+
| Recursion.java:101:16:101:23 | level2 calls level0 | Recursion.java:95:16:95:23 | level1 calls level2 : Boolean | Recursion.java:101:16:101:23 | level2 calls level0 | Found a recursion: |
74+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | Found a recursion: |
75+
| Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth : Boolean | Recursion.java:115:16:115:54 | directRecursiveNoDepth calls directRecursiveNoDepth | Found a recursion: |

java/test/query-tests/security/Recursion/Recursion.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,25 @@ public boolean directRecursiveNoDepth(int anything, int depth) {
117117
}
118118

119119
class RecursiveCallLimited {
120-
// todook: recursion is limited
120+
// ok: recursion is limited
121121
public boolean directRecursiveDepth(int depth) {
122122
if (depth == 0) {
123123
return true;
124124
}
125125
return directRecursiveDepth(depth - 1);
126126
}
127127

128-
// todook: level0->level1->level2->level0 with bound
128+
// ok: recursion is limited
129+
public boolean directRecursiveComputedDepth(int depth) {
130+
if (depth == 0) {
131+
return true;
132+
}
133+
134+
int newDepth = depth - 2;
135+
return directRecursiveComputedDepth(newDepth);
136+
}
137+
138+
// ok: level0->level1->level2->level0 with bound
129139
public boolean level0D(int depth) {
130140
if (depth == 0) {
131141
return true;

0 commit comments

Comments
 (0)