Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8170f01

Browse files
committed
Query to detect uncontrolled thread resource consumption
1 parent 2cbad4a commit 8170f01

8 files changed

Lines changed: 315 additions & 0 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** Provides sink models related to pausing thread operations. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.java.dataflow.ExternalFlow
6+
7+
/** Thread pause data model in the new CSV format. */
8+
private class PauseThreadDataModel extends SinkModelCsv {
9+
override predicate row(string row) {
10+
row =
11+
[
12+
"java.lang;Thread;true;sleep;;;Argument[0];thread-pause",
13+
"java.util.concurrent;TimeUnit;true;sleep;;;Argument[0];thread-pause"
14+
]
15+
}
16+
}
17+
18+
/** A sink representing methods pausing a thread. */
19+
class PauseThreadSink extends DataFlow::Node {
20+
PauseThreadSink() { sinkNode(this, "thread-pause") }
21+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
public class ThreadResourceAbuse extends HttpServlet {
2+
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
3+
// Get thread pause time from request parameter
4+
String delayTimeStr = request.getParameter("DelayTime");
5+
try {
6+
int delayTime = Integer.valueOf(delayTimeStr);
7+
new SyncAction(delayTime).start();
8+
} catch (NumberFormatException e) {
9+
}
10+
}
11+
12+
class SyncAction extends Thread {
13+
int waitTime;
14+
15+
public SyncAction(int waitTime) {
16+
this.waitTime = waitTime;
17+
}
18+
19+
@Override
20+
public void run() {
21+
try {
22+
{
23+
// BAD: no boundary check on wait time
24+
Thread.sleep(waitTime);
25+
}
26+
27+
28+
{
29+
// GOOD: enforce an upper limit on wait time
30+
if (waitTime > 0 && waitTime < 5000) {
31+
Thread.sleep(waitTime);
32+
}
33+
}
34+
35+
//Do other updates
36+
} catch (InterruptedException e) {
37+
}
38+
}
39+
}
40+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p><code>Thread.sleep</code> method is used to pause the execution of current thread for specified
9+
time. When it is used to keep several relevant tasks in synchronization and the sleep time is
10+
user-controlled data, especially in the web application context, it can be abused to cause all
11+
of a server's threads to sleep, leading to denial of service.</p>
12+
</overview>
13+
14+
<recommendation>
15+
<p>To guard against this attack, consider specifying an upper range of allowed sleep time or adopting
16+
the producer/consumer design pattern with <code>Thread.wait</code> method to avoid performance
17+
problems or even resource exhaustion.</p>
18+
</recommendation>
19+
20+
<example>
21+
<p>The following example shows the bad situation and the good situation respectively. In bad situation,
22+
a thread is spawned with the sleep time directly from user input. In good situation, an upper range
23+
check on maximum allowed sleep time is enforced.</p>
24+
<sample src="ThreadResourceAbuse.java" />
25+
</example>
26+
27+
<references>
28+
<li>
29+
snyk:
30+
<a href="https://snyk.io/vuln/SNYK-JAVA-COMGOOGLECODEGWTUPLOAD-569506">Denial of Service (DoS)
31+
Affecting com.googlecode.gwtupload:gwtupload artifact</a>.
32+
</li>
33+
<li>
34+
gwtupload
35+
<a href="https://github.com/manolo/gwtupload/issues/33">[Fix DOS issue] Updating the
36+
AbstractUploadListener.java file</a>.
37+
</li>
38+
</references>
39+
</qhelp>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @name Uncontrolled thread resource consumption
3+
* @description Use user input directly to control thread sleep time could lead to performance problems
4+
* or even resource exhaustion.
5+
* @kind path-problem
6+
* @id java/thread-resource-abuse
7+
* @tags security
8+
* external/cwe/cwe-400
9+
*/
10+
11+
import java
12+
import ThreadPauseSink
13+
import semmle.code.java.dataflow.FlowSources
14+
import DataFlow::PathGraph
15+
16+
/** The `getInitParameter` method of servlet or JSF. */
17+
class GetInitParameter extends Method {
18+
GetInitParameter() {
19+
(
20+
this.getDeclaringType()
21+
.getASupertype*()
22+
.hasQualifiedName(["javax.servlet", "jakarta.servlet"],
23+
["FilterConfig", "Registration", "ServletConfig", "ServletContext"]) or
24+
this.getDeclaringType()
25+
.getASupertype*()
26+
.hasQualifiedName(["javax.faces.context", "jakarta.faces.context"], "ExternalContext")
27+
) and
28+
this.getName() = "getInitParameter"
29+
}
30+
}
31+
32+
/** An access to the `getInitParameter` method. */
33+
class GetInitParameterAccess extends MethodAccess {
34+
GetInitParameterAccess() { this.getMethod() instanceof GetInitParameter }
35+
}
36+
37+
/* Init parameter input of a Java EE web application. */
38+
class InitParameterInput extends LocalUserInput {
39+
InitParameterInput() { this.asExpr() instanceof GetInitParameterAccess }
40+
}
41+
42+
private class LessThanSanitizer extends DataFlow::BarrierGuard {
43+
LessThanSanitizer() { this instanceof LTExpr }
44+
45+
override predicate checks(Expr e, boolean branch) {
46+
e = this.(LTExpr).getLeftOperand() and
47+
branch = true
48+
}
49+
}
50+
51+
/** Taint configuration of uncontrolled thread resource consumption. */
52+
class ThreadResourceAbuse extends TaintTracking::Configuration {
53+
ThreadResourceAbuse() { this = "ThreadResourceAbuse" }
54+
55+
override predicate isSource(DataFlow::Node source) {
56+
source instanceof RemoteFlowSource or source instanceof LocalUserInput
57+
}
58+
59+
override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink }
60+
61+
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
62+
exists(ConditionalExpr ce | ce.getAChildExpr() = node1.asExpr() and ce = node2.asExpr()) // request.getParameter("nodelay") != null ? 0 : sleepTime
63+
or
64+
exists(
65+
Method rm, ClassInstanceExpr ce, Argument arg, FieldAccess fa // thread.start() invokes the run() method of thread implementation
66+
|
67+
rm.hasName("run") and
68+
ce.getConstructedType() = rm.getSourceDeclaration().getDeclaringType() and
69+
ce.getConstructedType().getASupertype*().hasQualifiedName("java.lang", "Runnable") and
70+
ce.getAnArgument() = arg and
71+
fa = rm.getAnAccessedField().getAnAccess() and
72+
arg.getType() = fa.getField().getType() and
73+
node1.asExpr() = arg and
74+
node2.asExpr() = fa
75+
)
76+
}
77+
78+
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
79+
guard instanceof LessThanSanitizer // if (sleepTime > 0 && sleepTime < 5000) { ... }
80+
}
81+
}
82+
83+
from DataFlow::PathNode source, DataFlow::PathNode sink, ThreadResourceAbuse conf
84+
where conf.hasFlowPath(source, sink)
85+
select sink.getNode(), source, sink,
86+
"Vulnerability of uncontrolled resource consumption due to $@.", source.getNode(),
87+
"user-provided value"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
edges
2+
| ThreadResourceAbuse.java:15:25:15:57 | getParameter(...) : String | ThreadResourceAbuse.java:18:28:18:36 | delayTime : Number |
3+
| ThreadResourceAbuse.java:18:28:18:36 | delayTime : Number | ThreadResourceAbuse.java:62:18:62:25 | waitTime |
4+
| ThreadResourceAbuse.java:25:25:25:73 | getInitParameter(...) : String | ThreadResourceAbuse.java:28:28:28:36 | delayTime : Number |
5+
| ThreadResourceAbuse.java:28:28:28:36 | delayTime : Number | ThreadResourceAbuse.java:62:18:62:25 | waitTime |
6+
| ThreadResourceAbuse.java:97:27:97:43 | getValue(...) : String | ThreadResourceAbuse.java:100:34:100:42 | delayTime |
7+
nodes
8+
| ThreadResourceAbuse.java:15:25:15:57 | getParameter(...) : String | semmle.label | getParameter(...) : String |
9+
| ThreadResourceAbuse.java:18:28:18:36 | delayTime : Number | semmle.label | delayTime : Number |
10+
| ThreadResourceAbuse.java:25:25:25:73 | getInitParameter(...) : String | semmle.label | getInitParameter(...) : String |
11+
| ThreadResourceAbuse.java:28:28:28:36 | delayTime : Number | semmle.label | delayTime : Number |
12+
| ThreadResourceAbuse.java:62:18:62:25 | waitTime | semmle.label | waitTime |
13+
| ThreadResourceAbuse.java:97:27:97:43 | getValue(...) : String | semmle.label | getValue(...) : String |
14+
| ThreadResourceAbuse.java:100:34:100:42 | delayTime | semmle.label | delayTime |
15+
#select
16+
| ThreadResourceAbuse.java:62:18:62:25 | waitTime | ThreadResourceAbuse.java:15:25:15:57 | getParameter(...) : String | ThreadResourceAbuse.java:62:18:62:25 | waitTime | Vulnerability of uncontrolled resource consumption due to $@. | ThreadResourceAbuse.java:15:25:15:57 | getParameter(...) | user-provided value |
17+
| ThreadResourceAbuse.java:62:18:62:25 | waitTime | ThreadResourceAbuse.java:25:25:25:73 | getInitParameter(...) : String | ThreadResourceAbuse.java:62:18:62:25 | waitTime | Vulnerability of uncontrolled resource consumption due to $@. | ThreadResourceAbuse.java:25:25:25:73 | getInitParameter(...) | user-provided value |
18+
| ThreadResourceAbuse.java:100:34:100:42 | delayTime | ThreadResourceAbuse.java:97:27:97:43 | getValue(...) : String | ThreadResourceAbuse.java:100:34:100:42 | delayTime | Vulnerability of uncontrolled resource consumption due to $@. | ThreadResourceAbuse.java:97:27:97:43 | getValue(...) | user-provided value |
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package test.cwe400.cwe.examples;
2+
3+
import java.io.IOException;
4+
import java.util.concurrent.TimeUnit;
5+
6+
import javax.servlet.ServletException;
7+
import javax.servlet.http.Cookie;
8+
import javax.servlet.http.HttpServlet;
9+
import javax.servlet.http.HttpServletRequest;
10+
import javax.servlet.http.HttpServletResponse;
11+
12+
public class ThreadResourceAbuse extends HttpServlet {
13+
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
14+
// Get thread pause time from request parameter
15+
String delayTimeStr = request.getParameter("DelayTime");
16+
try {
17+
int delayTime = Integer.valueOf(delayTimeStr);
18+
new UncheckedSyncAction(delayTime).start();
19+
} catch (NumberFormatException e) {
20+
}
21+
}
22+
23+
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
24+
// Get thread pause time from init container parameter
25+
String delayTimeStr = getServletContext().getInitParameter("DelayTime");
26+
try {
27+
int delayTime = Integer.valueOf(delayTimeStr);
28+
new UncheckedSyncAction(delayTime).start();
29+
} catch (NumberFormatException e) {
30+
}
31+
}
32+
33+
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
34+
// Get thread pause time from request cookie
35+
Cookie[] cookies = request.getCookies();
36+
37+
for ( int i=0; i<cookies.length; i++) {
38+
Cookie cookie = cookies[i];
39+
40+
if (cookie.getName().equals("DelayTime")) {
41+
String delayTimeStr = cookie.getValue();
42+
try {
43+
int delayTime = Integer.valueOf(delayTimeStr);
44+
new CheckedSyncAction(delayTime).start();
45+
} catch (NumberFormatException e) {
46+
}
47+
}
48+
}
49+
}
50+
51+
class UncheckedSyncAction extends Thread {
52+
int waitTime;
53+
54+
public UncheckedSyncAction(int waitTime) {
55+
this.waitTime = waitTime;
56+
}
57+
58+
@Override
59+
// BAD: no boundary check on wait time
60+
public void run() {
61+
try {
62+
Thread.sleep(waitTime);
63+
// Do other updates
64+
} catch (InterruptedException e) {
65+
}
66+
}
67+
}
68+
69+
class CheckedSyncAction extends Thread {
70+
int waitTime;
71+
72+
public CheckedSyncAction(int waitTime) {
73+
this.waitTime = waitTime;
74+
}
75+
76+
// GOOD: enforce an upper limit on wait time
77+
@Override
78+
public void run() {
79+
try {
80+
if (waitTime > 0 && waitTime < 5000) {
81+
Thread.sleep(waitTime);
82+
// Do other updates
83+
}
84+
} catch (InterruptedException e) {
85+
}
86+
}
87+
}
88+
89+
protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
90+
// Get thread pause time from request cookie
91+
Cookie[] cookies = request.getCookies();
92+
93+
for ( int i=0; i<cookies.length; i++) {
94+
Cookie cookie = cookies[i];
95+
96+
if (cookie.getName().equals("DelayTime")) {
97+
String delayTimeStr = cookie.getValue();
98+
try {
99+
int delayTime = Integer.valueOf(delayTimeStr);
100+
TimeUnit.MILLISECONDS.sleep(delayTime);
101+
// Do other updates
102+
} catch (NumberFormatException ne) {
103+
} catch (InterruptedException ie) {
104+
}
105+
}
106+
}
107+
}
108+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-400/ThreadResourceAbuse.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4

0 commit comments

Comments
 (0)