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
1212
1313import java
1414
15+
1516predicate 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
2122class 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+ */
0 commit comments