mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Move duplicate code to the shared library and update qldoc
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @name Uncontrolled thread resource consumption from local input source
|
* @name Uncontrolled thread resource consumption from local input source
|
||||||
* @description Use user input directly to control thread sleep time could lead to performance problems
|
* @description Using user input directly to control a thread's sleep time could lead to
|
||||||
* or even resource exhaustion.
|
* performance problems or even resource exhaustion.
|
||||||
* @kind path-problem
|
* @kind path-problem
|
||||||
* @id java/thread-resource-abuse
|
* @id java/thread-resource-abuse
|
||||||
* @problem.severity recommendation
|
* @problem.severity recommendation
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import ThreadPauseSink
|
import ThreadResourceAbuse
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
@@ -40,18 +40,6 @@ class InitParameterInput extends LocalUserInput {
|
|||||||
InitParameterInput() { this.asExpr() instanceof GetInitParameterAccess }
|
InitParameterInput() { this.asExpr() instanceof GetInitParameterAccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LessThanSanitizer extends DataFlow::BarrierGuard {
|
|
||||||
LessThanSanitizer() { this instanceof ComparisonExpr }
|
|
||||||
|
|
||||||
override predicate checks(Expr e, boolean branch) {
|
|
||||||
e = this.(ComparisonExpr).getLesserOperand() and
|
|
||||||
branch = true
|
|
||||||
or
|
|
||||||
e = this.(ComparisonExpr).getGreaterOperand() and
|
|
||||||
branch = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Taint configuration of uncontrolled thread resource consumption from local user input. */
|
/** Taint configuration of uncontrolled thread resource consumption from local user input. */
|
||||||
class ThreadResourceAbuse extends TaintTracking::Configuration {
|
class ThreadResourceAbuse extends TaintTracking::Configuration {
|
||||||
ThreadResourceAbuse() { this = "ThreadResourceAbuse" }
|
ThreadResourceAbuse() { this = "ThreadResourceAbuse" }
|
||||||
@@ -60,34 +48,8 @@ class ThreadResourceAbuse extends TaintTracking::Configuration {
|
|||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink }
|
override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink }
|
||||||
|
|
||||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||||
exists(
|
any(ThreadResourceAbuseAdditionalTaintStep r).propagatesTaint(pred, succ)
|
||||||
Method rm, ClassInstanceExpr ce, Argument arg, Parameter p, FieldAccess fa, int i // thread.start() invokes the run() method of thread implementation
|
|
||||||
|
|
|
||||||
rm.hasName("run") and
|
|
||||||
ce.getConstructedType().getSourceDeclaration() = rm.getSourceDeclaration().getDeclaringType() and
|
|
||||||
ce.getConstructedType().getASupertype*().hasQualifiedName("java.lang", "Runnable") and
|
|
||||||
ce.getArgument(i) = arg and
|
|
||||||
ce.getConstructor().getParameter(i) = p and
|
|
||||||
fa.getEnclosingCallable() = rm and
|
|
||||||
DataFlow::localExprFlow(p.getAnAccess(), fa.getField().getAnAssignedValue()) and
|
|
||||||
node1.asExpr() = arg and
|
|
||||||
node2.asExpr() = fa
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Method um, VarAccess va, FieldAccess fa, Constructor ce, AssignExpr ar |
|
|
||||||
um.getDeclaringType()
|
|
||||||
.getASupertype*()
|
|
||||||
.hasQualifiedName("org.apache.commons.fileupload", "ProgressListener") and
|
|
||||||
um.hasName("update") and
|
|
||||||
fa.getEnclosingCallable() = um and
|
|
||||||
ce.getDeclaringType() = um.getDeclaringType() and
|
|
||||||
va = ce.getAParameter().getAnAccess() and
|
|
||||||
node1.asExpr() = va and
|
|
||||||
node2.asExpr() = fa and
|
|
||||||
ar.getSource() = va and
|
|
||||||
ar.getDest() = fa.getField().getAnAccess()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSanitizer(DataFlow::Node node) {
|
override predicate isSanitizer(DataFlow::Node node) {
|
||||||
@@ -106,6 +68,5 @@ class ThreadResourceAbuse extends TaintTracking::Configuration {
|
|||||||
|
|
||||||
from DataFlow::PathNode source, DataFlow::PathNode sink, ThreadResourceAbuse conf
|
from DataFlow::PathNode source, DataFlow::PathNode sink, ThreadResourceAbuse conf
|
||||||
where conf.hasFlowPath(source, sink)
|
where conf.hasFlowPath(source, sink)
|
||||||
select sink.getNode(), source, sink,
|
select sink.getNode(), source, sink, "Possible uncontrolled resource consumption due to $@.",
|
||||||
"Vulnerability of uncontrolled resource consumption due to $@.", source.getNode(),
|
source.getNode(), "local user-provided value"
|
||||||
"local user-provided value"
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/** Provides sink models related to pausing thread operations. */
|
|
||||||
|
|
||||||
import java
|
|
||||||
import semmle.code.java.dataflow.DataFlow
|
|
||||||
import semmle.code.java.dataflow.ExternalFlow
|
|
||||||
|
|
||||||
/** `java.lang.Math` data model for value comparison in the new CSV format. */
|
|
||||||
private class MathCompDataModel extends SummaryModelCsv {
|
|
||||||
override predicate row(string row) {
|
|
||||||
row =
|
|
||||||
[
|
|
||||||
"java.lang;Math;false;min;;;Argument[0..1];ReturnValue;value",
|
|
||||||
"java.lang;Math;false;max;;;Argument[0..1];ReturnValue;value"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Thread pause data model in the new CSV format. */
|
|
||||||
private class PauseThreadDataModel extends SinkModelCsv {
|
|
||||||
override predicate row(string row) {
|
|
||||||
row =
|
|
||||||
[
|
|
||||||
"java.lang;Thread;true;sleep;;;Argument[0];thread-pause",
|
|
||||||
"java.util.concurrent;TimeUnit;true;sleep;;;Argument[0];thread-pause"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A sink representing methods pausing a thread. */
|
|
||||||
class PauseThreadSink extends DataFlow::Node {
|
|
||||||
PauseThreadSink() { sinkNode(this, "thread-pause") }
|
|
||||||
}
|
|
||||||
@@ -5,22 +5,22 @@
|
|||||||
|
|
||||||
|
|
||||||
<overview>
|
<overview>
|
||||||
<p><code>Thread.sleep</code> method is used to pause the execution of current thread for specified
|
<p>The <code>Thread.sleep</code> method is used to pause the execution of current thread for
|
||||||
time. When it is used to keep several relevant tasks in synchronization and the sleep time is
|
specified time. When the sleep time is user-controlled, especially in the web application context,
|
||||||
user-controlled data, especially in the web application context, it can be abused to cause all
|
it can be abused to cause all of a server's threads to sleep, leading to denial of service.</p>
|
||||||
of a server's threads to sleep, leading to denial of service.</p>
|
|
||||||
</overview>
|
</overview>
|
||||||
|
|
||||||
<recommendation>
|
<recommendation>
|
||||||
<p>To guard against this attack, consider specifying an upper range of allowed sleep time or adopting
|
<p>To guard against this attack, consider specifying an upper range of allowed sleep time or adopting
|
||||||
the producer/consumer design pattern with <code>Object.wait</code> method to avoid performance
|
the producer/consumer design pattern with <code>Object.wait</code> method to avoid performance
|
||||||
problems or even resource exhaustion.</p>
|
problems or even resource exhaustion. For more information, refer to the concurrency tutorial of Oracle
|
||||||
|
listed below or <code>java/ql/src/Likely Bugs/Concurrency</code> queries of CodeQL.</p>
|
||||||
</recommendation>
|
</recommendation>
|
||||||
|
|
||||||
<example>
|
<example>
|
||||||
<p>The following example shows the bad situation and the good situation respectively. In bad situation,
|
<p>The following example shows a bad situation and a good situation respectively. In the bad situation,
|
||||||
a thread is spawned with the sleep time directly from user input. In good situation, an upper range
|
a thread is spawned with a sleep time coming directly from user input. In the good situation, an upper
|
||||||
check on maximum allowed sleep time is enforced.</p>
|
range check on the maximum sleep time allowed is enforced.</p>
|
||||||
<sample src="ThreadResourceAbuse.java" />
|
<sample src="ThreadResourceAbuse.java" />
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
@@ -40,5 +40,9 @@ The blog of a gypsy engineer:
|
|||||||
<a href="https://blog.gypsyengineer.com/en/security/cve-2019-17555-dos-via-retry-after-header-in-apache-olingo.html">
|
<a href="https://blog.gypsyengineer.com/en/security/cve-2019-17555-dos-via-retry-after-header-in-apache-olingo.html">
|
||||||
CVE-2019-17555: DoS via Retry-After header in Apache Olingo</a>.
|
CVE-2019-17555: DoS via Retry-After header in Apache Olingo</a>.
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
Oracle:
|
||||||
|
<a href="https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html">The Java Concurrency Tutorials</a>
|
||||||
|
</li>
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @name Uncontrolled thread resource consumption
|
* @name Uncontrolled thread resource consumption
|
||||||
* @description Use user input directly to control thread sleep time could lead to performance problems
|
* @description Using user input directly to control a thread's sleep time could lead to
|
||||||
* or even resource exhaustion.
|
* performance problems or even resource exhaustion.
|
||||||
* @kind path-problem
|
* @kind path-problem
|
||||||
* @id java/thread-resource-abuse
|
* @id java/thread-resource-abuse
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
@@ -10,22 +10,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import ThreadPauseSink
|
import ThreadResourceAbuse
|
||||||
import semmle.code.java.dataflow.FlowSources
|
import semmle.code.java.dataflow.FlowSources
|
||||||
import DataFlow::PathGraph
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
private class LessThanSanitizer extends DataFlow::BarrierGuard {
|
|
||||||
LessThanSanitizer() { this instanceof ComparisonExpr }
|
|
||||||
|
|
||||||
override predicate checks(Expr e, boolean branch) {
|
|
||||||
e = this.(ComparisonExpr).getLesserOperand() and
|
|
||||||
branch = true
|
|
||||||
or
|
|
||||||
e = this.(ComparisonExpr).getGreaterOperand() and
|
|
||||||
branch = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Taint configuration of uncontrolled thread resource consumption. */
|
/** Taint configuration of uncontrolled thread resource consumption. */
|
||||||
class ThreadResourceAbuse extends TaintTracking::Configuration {
|
class ThreadResourceAbuse extends TaintTracking::Configuration {
|
||||||
ThreadResourceAbuse() { this = "ThreadResourceAbuse" }
|
ThreadResourceAbuse() { this = "ThreadResourceAbuse" }
|
||||||
@@ -34,34 +22,8 @@ class ThreadResourceAbuse extends TaintTracking::Configuration {
|
|||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink }
|
override predicate isSink(DataFlow::Node sink) { sink instanceof PauseThreadSink }
|
||||||
|
|
||||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||||
exists(
|
any(ThreadResourceAbuseAdditionalTaintStep r).propagatesTaint(pred, succ)
|
||||||
Method rm, ClassInstanceExpr ce, Argument arg, Parameter p, FieldAccess fa, int i // thread.start() invokes the run() method of thread implementation
|
|
||||||
|
|
|
||||||
rm.hasName("run") and
|
|
||||||
ce.getConstructedType().getSourceDeclaration() = rm.getSourceDeclaration().getDeclaringType() and
|
|
||||||
ce.getConstructedType().getASupertype*().hasQualifiedName("java.lang", "Runnable") and
|
|
||||||
ce.getArgument(i) = arg and
|
|
||||||
ce.getConstructor().getParameter(i) = p and
|
|
||||||
fa.getEnclosingCallable() = rm and
|
|
||||||
DataFlow::localExprFlow(p.getAnAccess(), fa.getField().getAnAssignedValue()) and
|
|
||||||
node1.asExpr() = arg and
|
|
||||||
node2.asExpr() = fa
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Method um, VarAccess va, FieldAccess fa, Constructor ce, AssignExpr ar |
|
|
||||||
um.getDeclaringType()
|
|
||||||
.getASupertype*()
|
|
||||||
.hasQualifiedName("org.apache.commons.fileupload", "ProgressListener") and
|
|
||||||
um.hasName("update") and
|
|
||||||
fa.getEnclosingCallable() = um and
|
|
||||||
ce.getDeclaringType() = um.getDeclaringType() and
|
|
||||||
va = ce.getAParameter().getAnAccess() and
|
|
||||||
node1.asExpr() = va and
|
|
||||||
node2.asExpr() = fa and
|
|
||||||
ar.getSource() = va and
|
|
||||||
ar.getDest() = fa.getField().getAnAccess()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSanitizer(DataFlow::Node node) {
|
override predicate isSanitizer(DataFlow::Node node) {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/** Provides sink models and classes related to pausing thread operations. */
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.DataFlow
|
||||||
|
import semmle.code.java.dataflow.ExternalFlow
|
||||||
|
|
||||||
|
/** `java.lang.Math` data model for value comparison in the new CSV format. */
|
||||||
|
private class MathCompDataModel extends SummaryModelCsv {
|
||||||
|
override predicate row(string row) {
|
||||||
|
row =
|
||||||
|
[
|
||||||
|
"java.lang;Math;false;min;;;Argument[0..1];ReturnValue;value",
|
||||||
|
"java.lang;Math;false;max;;;Argument[0..1];ReturnValue;value"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Thread pause data model in the new CSV format. */
|
||||||
|
private class PauseThreadDataModel extends SinkModelCsv {
|
||||||
|
override predicate row(string row) {
|
||||||
|
row =
|
||||||
|
[
|
||||||
|
"java.lang;Thread;true;sleep;;;Argument[0];thread-pause",
|
||||||
|
"java.util.concurrent;TimeUnit;true;sleep;;;Argument[0];thread-pause"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A sink representing methods pausing a thread. */
|
||||||
|
class PauseThreadSink extends DataFlow::Node {
|
||||||
|
PauseThreadSink() { sinkNode(this, "thread-pause") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A sanitizer for lessThan check. */
|
||||||
|
class LessThanSanitizer extends DataFlow::BarrierGuard instanceof ComparisonExpr {
|
||||||
|
override predicate checks(Expr e, boolean branch) {
|
||||||
|
e = this.(ComparisonExpr).getLesserOperand() and
|
||||||
|
branch = true
|
||||||
|
or
|
||||||
|
e = this.(ComparisonExpr).getGreaterOperand() and
|
||||||
|
branch = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unit class for adding additional taint steps that are specific to thread resource abuse.
|
||||||
|
*/
|
||||||
|
class ThreadResourceAbuseAdditionalTaintStep extends Unit {
|
||||||
|
/**
|
||||||
|
* Holds if the step from `pred` to `succ` should be considered a taint
|
||||||
|
* step for thread resource abuse.
|
||||||
|
*/
|
||||||
|
abstract predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RunnableAdditionalTaintStep extends ThreadResourceAbuseAdditionalTaintStep {
|
||||||
|
override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) {
|
||||||
|
exists(
|
||||||
|
Method rm, ClassInstanceExpr ce, Argument arg, Parameter p, FieldAccess fa, int i // thread.start() invokes the run() method of thread implementation
|
||||||
|
|
|
||||||
|
rm.hasName("run") and
|
||||||
|
ce.getConstructedType().getSourceDeclaration() = rm.getSourceDeclaration().getDeclaringType() and
|
||||||
|
ce.getConstructedType().getASupertype*().hasQualifiedName("java.lang", "Runnable") and
|
||||||
|
ce.getArgument(i) = arg and
|
||||||
|
ce.getConstructor().getParameter(i) = p and
|
||||||
|
fa.getEnclosingCallable() = rm and
|
||||||
|
DataFlow::localExprFlow(p.getAnAccess(), fa.getField().getAnAssignedValue()) and
|
||||||
|
pred.asExpr() = arg and
|
||||||
|
succ.asExpr() = fa
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ApacheFileUploadAdditionalTaintStep extends ThreadResourceAbuseAdditionalTaintStep {
|
||||||
|
override predicate propagatesTaint(DataFlow::Node pred, DataFlow::Node succ) {
|
||||||
|
exists(Method um, VarAccess va, FieldAccess fa, Constructor ce, AssignExpr ar |
|
||||||
|
um.getDeclaringType()
|
||||||
|
.getASupertype*()
|
||||||
|
.hasQualifiedName("org.apache.commons.fileupload", "ProgressListener") and
|
||||||
|
um.hasName("update") and
|
||||||
|
fa.getEnclosingCallable() = um and
|
||||||
|
ce.getDeclaringType() = um.getDeclaringType() and
|
||||||
|
va = ce.getAParameter().getAnAccess() and
|
||||||
|
pred.asExpr() = va and
|
||||||
|
succ.asExpr() = fa and
|
||||||
|
ar.getSource() = va and
|
||||||
|
ar.getDest() = fa.getField().getAnAccess()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user