Skip to content

Commit c3213e4

Browse files
committed
update recursion query to path-problem, deduplicate results
1 parent a0e61b4 commit c3213e4

File tree

2 files changed

+66
-7
lines changed

2 files changed

+66
-7
lines changed

java/src/security/Recursion/Recursion.ql

Lines changed: 33 additions & 7 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
@@ -12,26 +12,52 @@
1212

1313
import java
1414

15+
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

2122
class RecursiveMethod extends Method {
23+
Call entryCall;
24+
Call lastCall;
25+
2226
RecursiveMethod() {
23-
exists(Method m | this.calls+(m) and this = m)
27+
not isTestPackage(this.getDeclaringType())
28+
and entryCall.getEnclosingCallable() = this
29+
and edges+(entryCall, lastCall) and lastCall.getCallee() = this
2430
}
31+
32+
Call getEntryCall() { result = entryCall}
33+
34+
Call getLastCall() { result = lastCall}
35+
}
36+
37+
query predicate edges(Call a, Call b) {
38+
exists(Callable c |
39+
a.getCallee() = c and b.getCaller() = c
40+
)
2541
}
2642

43+
from RecursiveMethod recursiveMethod
44+
where
45+
/* for a single recursion loop we would return multiple results so we deduplicate redundant findings
46+
returning only the one starting from a method with "greatest" name
47+
*/
48+
not exists(RecursiveMethod rm2 |
49+
edges+(rm2.getEntryCall(), recursiveMethod.getLastCall())
50+
and exists(Call x | edges(recursiveMethod.getLastCall(), x) and edges(x, rm2.getEntryCall()))
51+
and rm2 != recursiveMethod
52+
and rm2.getQualifiedName() > recursiveMethod.getQualifiedName()
53+
)
54+
select recursiveMethod.getLastCall(), recursiveMethod.getEntryCall(), recursiveMethod.getLastCall(),
55+
"Found a recursion path from/to method $@", recursiveMethod, recursiveMethod.toString()
56+
2757
/**
2858
* TODO ideas:
2959
* - limit results to methods that take any user input
3060
* - check if recursive calls are bounded (takes argument that is decremented for every call)
3161
* - do not return methods that have calls to self (or unbounded recursion) that are conditional
3262
* - gather and print whole call graph (list of calls from recursiveMethod to itself)
33-
*/
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+
*/

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,39 @@ private boolean someCondition() {
106106
}
107107
}
108108

109+
class RecursiveCallNonLinear {
110+
// finding: level0->...->level0
111+
public boolean level0() {
112+
if (someOtherCondition()) {
113+
return true;
114+
}
115+
if (someCondition()) {
116+
return level1();
117+
}
118+
return level2();
119+
}
120+
public boolean level1() {
121+
if (someCondition()) {
122+
return true;
123+
}
124+
return level2();
125+
}
126+
public boolean level2() {
127+
if (someCondition()) {
128+
return level1();
129+
}
130+
return level0();
131+
}
132+
133+
private boolean someCondition() {
134+
return false;
135+
}
136+
137+
private boolean someOtherCondition() {
138+
return true;
139+
}
140+
}
141+
109142
class RecursiveCallWronglyLimited {
110143
// finding: recursion is not limited
111144
public boolean directRecursiveNoDepth(int anything, int depth) {

0 commit comments

Comments
 (0)