Skip to content

Commit

Permalink
update recursion query to path-problem, deduplicate results
Browse files Browse the repository at this point in the history
  • Loading branch information
GrosQuildu committed Nov 18, 2024
1 parent a0e61b4 commit c3213e4
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 7 deletions.
40 changes: 33 additions & 7 deletions java/src/security/Recursion/Recursion.ql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @name Recursive functions
* @id tob/java/unbounded-recursion
* @description Detects possibly unbounded recursive calls
* @kind problem
* @kind path-problem
* @tags security
* @precision low
* @problem.severity warning
Expand All @@ -12,26 +12,52 @@

import java


predicate isTestPackage(RefType referenceType) {
referenceType.getPackage().getName().toLowerCase().matches("%test%") or
referenceType.getPackage().getName().toLowerCase().matches("%benchmark%") or
referenceType.getName().toLowerCase().matches("%test%")
}

class RecursiveMethod extends Method {
Call entryCall;
Call lastCall;

RecursiveMethod() {
exists(Method m | this.calls+(m) and this = m)
not isTestPackage(this.getDeclaringType())
and entryCall.getEnclosingCallable() = this
and edges+(entryCall, lastCall) and lastCall.getCallee() = this
}

Call getEntryCall() { result = entryCall}

Call getLastCall() { result = lastCall}
}

query predicate edges(Call a, Call b) {
exists(Callable c |
a.getCallee() = c and b.getCaller() = c
)
}

from RecursiveMethod recursiveMethod
where
/* for a single recursion loop we would return multiple results so we deduplicate redundant findings
returning only the one starting from a method with "greatest" name
*/
not exists(RecursiveMethod rm2 |
edges+(rm2.getEntryCall(), recursiveMethod.getLastCall())
and exists(Call x | edges(recursiveMethod.getLastCall(), x) and edges(x, rm2.getEntryCall()))
and rm2 != recursiveMethod
and rm2.getQualifiedName() > recursiveMethod.getQualifiedName()
)
select recursiveMethod.getLastCall(), recursiveMethod.getEntryCall(), recursiveMethod.getLastCall(),
"Found a recursion path from/to method $@", recursiveMethod, recursiveMethod.toString()

/**
* TODO ideas:
* - limit results to methods that take any user input
* - check if recursive calls are bounded (takes argument that is decremented for every call)
* - do not return methods that have calls to self (or unbounded recursion) that are conditional
* - gather and print whole call graph (list of calls from recursiveMethod to itself)
*/
from RecursiveMethod recursiveMethod
where
not isTestPackage(recursiveMethod.getDeclaringType())
select recursiveMethod, "Method $@ has unbounded, possibly infinite recursive calls", recursiveMethod, recursiveMethod.toString()
*/
33 changes: 33 additions & 0 deletions java/test/query-tests/security/Recursion/Recursion.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,39 @@ private boolean someCondition() {
}
}

class RecursiveCallNonLinear {
// finding: level0->...->level0
public boolean level0() {
if (someOtherCondition()) {
return true;
}
if (someCondition()) {
return level1();
}
return level2();
}
public boolean level1() {
if (someCondition()) {
return true;
}
return level2();
}
public boolean level2() {
if (someCondition()) {
return level1();
}
return level0();
}

private boolean someCondition() {
return false;
}

private boolean someOtherCondition() {
return true;
}
}

class RecursiveCallWronglyLimited {
// finding: recursion is not limited
public boolean directRecursiveNoDepth(int anything, int depth) {
Expand Down

0 comments on commit c3213e4

Please sign in to comment.