-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
342 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{ | ||
"provide": [ | ||
"cpp/*/qlpack.yml", | ||
"go/*/qlpack.yml" | ||
"go/*/qlpack.yml", | ||
"java/*/qlpack.yml" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
lockVersion: 1.0.0 | ||
dependencies: | ||
codeql/dataflow: | ||
version: 1.1.5 | ||
codeql/java-all: | ||
version: 4.2.0 | ||
codeql/mad: | ||
version: 1.0.11 | ||
codeql/rangeanalysis: | ||
version: 1.0.11 | ||
codeql/regex: | ||
version: 1.0.11 | ||
codeql/ssa: | ||
version: 1.0.11 | ||
codeql/threat-models: | ||
version: 1.0.11 | ||
codeql/tutorial: | ||
version: 1.0.11 | ||
codeql/typeflow: | ||
version: 1.0.11 | ||
codeql/typetracking: | ||
version: 1.0.11 | ||
codeql/util: | ||
version: 1.0.11 | ||
codeql/xml: | ||
version: 1.0.11 | ||
compiled: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
- description: Security queries for Java | ||
- queries: 'security' | ||
from: trailofbits/java-queries | ||
- exclude: | ||
tags contain: experimental |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
- description: Queries for Java | ||
- queries: '.' | ||
from: trailofbits/java-queries |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# Recursive functions | ||
Recursive functions are methods that call themselves either directly or indirectly through other functions. While recursion can be a powerful programming technique, unbounded recursion on user inputs can lead to stack overflow errors and program crashes, potentially enabling denial of service attacks. This query detects recursive patterns up to order 4. | ||
|
||
|
||
## Recommendation | ||
Review recursive functions and ensure that they are either: - Not processing user-controlled data - The data has been properly sanitized before recursing - The recursion has an explicit depth limit | ||
|
||
Consider replacing recursion with iterative alternatives where possible. | ||
|
||
|
||
## Example | ||
|
||
```java | ||
// From https://github.com/x-stream/xstream/blob/dfa1d35462fe84412ee72a9b0cf5b5c633086520/xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java#L165 | ||
private Token readToken() { | ||
// ... | ||
try { | ||
final Token token = tokenFormatter.read(in); | ||
switch (token.getType()) { | ||
case Token.TYPE_MAP_ID_TO_VALUE: // 0x2 | ||
idRegistry.put(token.getId(), token.getValue()); | ||
return readToken(); // Next one please. | ||
default: | ||
return token; | ||
} | ||
} catch (final IOException e) { | ||
throw new StreamException(e); | ||
} | ||
// ... | ||
} | ||
``` | ||
In this example, a binary stream reader processes tokens recursively. | ||
|
||
For each new token \`0x2\`, the parser will create a new recursive call. If this stream is user-controlled, an attacker can generate too many stackframes and crash the application with a `StackOverflow` error. | ||
|
||
|
||
## References | ||
* Trail Of Bits Blog: [Low-effort denial of service with recursion](https://blog.trailofbits.com/2024/05/16/TODO/) | ||
* CWE-674: [Uncontrolled Recursion](https://cwe.mitre.org/data/definitions/674.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
--- | ||
name: trailofbits/java-queries | ||
description: CodeQL queries for Java developed by Trail of Bits | ||
authors: Trail of Bits | ||
version: 0.0.1 | ||
license: AGPL | ||
library: false | ||
extractor: java-kotlin | ||
dependencies: | ||
codeql/java-all: "*" | ||
suites: codeql-suites | ||
defaultSuiteFile: codeql-suites/tob-java-code-scanning.qls |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE qhelp SYSTEM "qhelp.dtd"> | ||
<qhelp> | ||
<overview> | ||
<p> | ||
Recursive functions are methods that call themselves either directly or indirectly through other functions. | ||
While recursion can be a powerful programming technique, unbounded recursion on user inputs can lead | ||
to stack overflow errors and program crashes, potentially enabling denial of service attacks. | ||
|
||
This query detects recursive patterns up to order 4. | ||
</p> | ||
|
||
</overview> | ||
<recommendation> | ||
<p> | ||
Review recursive functions and ensure that they are either: | ||
- Not processing user-controlled data | ||
- The data has been properly sanitized before recursing | ||
- The recursion has an explicit depth limit | ||
</p> | ||
<p> | ||
Consider replacing recursion with iterative alternatives where possible. | ||
</p> | ||
</recommendation> | ||
<example> | ||
<sample src="RecursiveCall.java" /> | ||
<p>In this example, a binary stream reader processes tokens recursively.</p> | ||
<p>For each new token `0x2`, the parser will create a new recursive call. | ||
If this stream is user-controlled, an attacker can generate too many stackframes | ||
and crash the application with a <code>StackOverflow</code> error.</p> | ||
</example> | ||
<references> | ||
<li> | ||
Trail Of Bits Blog: <a href="https://blog.trailofbits.com/2024/05/16/TODO/">Low-effort denial of service with recursion</a> | ||
</li> | ||
<li> | ||
CWE-674: <a href="https://cwe.mitre.org/data/definitions/674.html">Uncontrolled Recursion</a> | ||
</li> | ||
</references> | ||
</qhelp> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/** | ||
* @name Recursive functions | ||
* @id tob/java/recursion | ||
* @description Detects recursive calls | ||
* @kind problem | ||
* @tags security | ||
* @precision low | ||
* @problem.severity warning | ||
* @security-severity 3.0 | ||
* @group security | ||
*/ | ||
|
||
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%") | ||
} | ||
|
||
predicate isBefore(Method a, Method b) { | ||
a.getLocation().getStartLine() < b.getLocation().getStartLine() | ||
} | ||
|
||
abstract class RecursiveCall extends MethodCall { | ||
Method m1; | ||
Method m2; | ||
Method m3; | ||
Method m4; | ||
|
||
abstract string asString(); | ||
|
||
string toString(Method m) { | ||
result = m.getName() + "(" + m.getLocation().getStartLine() + ")" + " -> " | ||
} | ||
} | ||
|
||
class RecursiveCallOrder1 extends RecursiveCall { | ||
RecursiveCallOrder1() { | ||
this.getMethod() = this.getEnclosingCallable() and | ||
m1 = this.getMethod() | ||
} | ||
|
||
override string asString() { result = "Recursion(1): " + toString(m1) + m1.getName() } | ||
} | ||
|
||
class RecursiveCallOrder2 extends RecursiveCall { | ||
//RecursiveCallOrder1 { | ||
RecursiveCallOrder2() { | ||
m1 = this.getMethod() and | ||
m2 = m1.getACallee() and | ||
m2.getACallee() = m1 and | ||
isBefore(m1, m2) | ||
} | ||
|
||
override string asString() { | ||
result = "Recursion(2): " + toString(m1) + toString(m2) + m1.getName() | ||
} | ||
} | ||
|
||
class RecursiveCallOrder3 extends RecursiveCall { | ||
RecursiveCallOrder3() { | ||
m1 = this.getMethod() and | ||
m2 = m1.getACallee() and | ||
m3 = m2.getACallee() and | ||
m3.getACallee() = m1 and | ||
isBefore(m1, m2) and | ||
isBefore(m1, m3) | ||
} | ||
|
||
override string asString() { | ||
result = "Recursion(3): " + toString(m1) + toString(m2) + toString(m3) + m1.getName() | ||
} | ||
} | ||
|
||
class RecursiveCallOrder4 extends RecursiveCall { | ||
RecursiveCallOrder4() { | ||
m1 = this.getMethod() and | ||
m2 = m1.getACallee() and | ||
m3 = m2.getACallee() and | ||
m4 = m3.getACallee() and | ||
m4.getACallee() = m1 and | ||
isBefore(m1, m2) and | ||
isBefore(m1, m3) and | ||
isBefore(m1, m4) | ||
} | ||
|
||
override string asString() { | ||
result = | ||
"Recursion(4): " + toString(m1) + toString(m2) + toString(m3) + toString(m4) + m1.getName() | ||
} | ||
} | ||
|
||
from RecursiveCall recursiveCall | ||
where not isTestPackage(recursiveCall.getMethod().getDeclaringType()) | ||
select recursiveCall, "$@", recursiveCall, recursiveCall.asString() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// From https://github.com/x-stream/xstream/blob/dfa1d35462fe84412ee72a9b0cf5b5c633086520/xstream/src/java/com/thoughtworks/xstream/io/binary/BinaryStreamReader.java#L165 | ||
private Token readToken() { | ||
// ... | ||
try { | ||
final Token token = tokenFormatter.read(in); | ||
switch (token.getType()) { | ||
case Token.TYPE_MAP_ID_TO_VALUE: // 0x2 | ||
idRegistry.put(token.getId(), token.getValue()); | ||
return readToken(); // Next one please. | ||
default: | ||
return token; | ||
} | ||
} catch (final IOException e) { | ||
throw new StreamException(e); | ||
} | ||
// ... | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
lockVersion: 1.0.0 | ||
dependencies: | ||
codeql/dataflow: | ||
version: 1.1.5 | ||
codeql/java-all: | ||
version: 4.2.0 | ||
codeql/mad: | ||
version: 1.0.11 | ||
codeql/rangeanalysis: | ||
version: 1.0.11 | ||
codeql/regex: | ||
version: 1.0.11 | ||
codeql/ssa: | ||
version: 1.0.11 | ||
codeql/threat-models: | ||
version: 1.0.11 | ||
codeql/tutorial: | ||
version: 1.0.11 | ||
codeql/typeflow: | ||
version: 1.0.11 | ||
codeql/typetracking: | ||
version: 1.0.11 | ||
codeql/util: | ||
version: 1.0.11 | ||
codeql/xml: | ||
version: 1.0.11 | ||
compiled: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
name: trailofbits/java-tests | ||
authors: Trail of Bits | ||
license: AGPL | ||
extractor: java-kotlin | ||
tests: . | ||
dependencies: | ||
trailofbits/java-queries: "*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
| Recursion.java:8:16:8:20 | bar(...) | $@ | Recursion.java:8:16:8:20 | bar(...) | Recursion(2): bar(2) -> foo(7) -> bar | | ||
| Recursion.java:12:16:12:32 | directRecursive(...) | $@ | Recursion.java:12:16:12:32 | directRecursive(...) | Recursion(1): directRecursive(11) -> directRecursive | | ||
| Recursion.java:31:16:31:23 | level0(...) | $@ | Recursion.java:31:16:31:23 | level0(...) | Recursion(3): level0(15) -> level1(21) -> level2(27) -> level0 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
class RecursiveCall { | ||
public boolean bar() { | ||
boolean fooResult = foo(); | ||
return fooResult; | ||
} | ||
|
||
public boolean foo() { | ||
return bar(); | ||
} | ||
|
||
public boolean directRecursive() { | ||
return directRecursive(); | ||
} | ||
|
||
public boolean level0() { | ||
if (someCondition()) { | ||
return true; | ||
} | ||
return level1(); | ||
} | ||
public boolean level1() { | ||
if (someCondition()) { | ||
return true; | ||
} | ||
return level2(); | ||
} | ||
public boolean level2() { | ||
if (someCondition()) { | ||
return true; | ||
} | ||
return level0(); | ||
} | ||
|
||
private boolean someCondition() { | ||
return false; | ||
} | ||
} | ||
|
||
|
||
class NotRecursive { | ||
|
||
public static boolean foo() { | ||
return bar(); | ||
} | ||
|
||
public static boolean bar() { | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
security/Recursion/Recursion.ql |