Move duplicate code to the shared library and update qldoc

This commit is contained in:
luchua-bc
2021-11-23 03:06:26 +00:00
parent b6a6ed5ba3
commit ed78d39d61
5 changed files with 114 additions and 129 deletions

View File

@@ -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"

View File

@@ -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") }
}

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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()
)
}
}