According to the Java API \[[API 06|AA. Java References#API 06]\], interface {{java.util.concurrent.ExecutorService}}, method {{shutdownNow()}} documentation:
{quote}
Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. For example, typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.
{quote}
Consequently, care must be taken so that only interruptible tasks are submitted to the thread pool. When using worker threads that run untrusted code or dynamically loaded extension code, a way to interrupt the worker must be provided. For instance, a worker thread in a thread pool should generally have the following structure (adopted from \[[Goetz 06|AA. Java References#Goetz 06]\]):
{code:bgColor=#ccccff}
public void run() {
Throwable thrown = null;
try {
while (!isInterrupted())
runTask(getTaskFromWorkQueue());
} catch (Throwable e) {
thrown = e;
} finally {
threadExited(this, thrown); // Notify upper layers
}
}
{code}
The worker thread based architecture that uses interruptible workers permits the caller to cleanly terminate misbehaving sub-tasks. If the sub-tasks die because of runtime exceptions, then the upper layers can take some suitable action such as replacing the worker thread with a new one.
h2. Noncompliant Code Example (shutting down thread pools)
This noncompliant code example uses the {{SocketReader}} class defined earlier in the Compliant Solution (close socket connection) of the guideline [CON24-J. Ensure that threads and tasks performing blocking operations can be terminated] and submits it as a task to a thread pool defined in class {{PoolService}}.
{code:bgColor=#FFcccc}
public final class PoolService {
private final ExecutorService pool;
public PoolService(int poolSize) {
pool = Executors.newFixedThreadPool(poolSize);
}
public void doSomething() throws InterruptedException, IOException {
pool.submit(new SocketReader());
// ...
List<Runnable> awaitingTasks = pool.shutdownNow();
}
public static void main(String[] args) throws InterruptedException, IOException {
PoolService service = new PoolService(5);
service.doSomething();
}
}
public final class SocketReader implements Runnable {
private final Socket socket;
// ...
}
{code}
Because the task does not support interruption through the use of {{Thread.interrupted()}}, there is no guarantee that the {{shutdownNow()}} method will shutdown the thread pool. Using the {{shutdown()}} method does not fix the problem either, because it waits until all executing tasks have finished. {mc} I think the general policy is to avoid "will" {mc} Similarly, tasks that check a {{volatile}} flag to determine whether it is safe to shutdown will also be unresponsive to these methods. The guideline [CON13-J. Ensure that threads are stopped cleanly] provides more information on using a flag to terminate threads. {mc} I added a reference instead of explaining {mc}
h2. Compliant Solution (submit interruptible tasks)
Tasks that do not support interruption using {{Thread.interrupt()}} should not be submitted to a thread pool. This compliant solution submits the interruptible version of {{SocketReader}} discussed in the Compliant Solution (interruptible channel) of the guideline [CON24-J. Ensure that threads and tasks performing blocking operations can be terminated], to the thread pool.
{code:bgColor=#ccccff}
public final class PoolService {
// ...
}
public final class SocketReader implements Runnable {
private final SocketChannel sc;
// ...
}
{code}
{mc} Can we avoid "you" to be more formal and consistent with other guidelines?
Similarly, when attempting to cancel individual tasks within the thread pool using the {{Future.cancel()}} method, you must ensure that the tasks support interruption. If they do, then you must pass a {{boolean}} argument {{true}} to {{cancel()}}, otherwise you must pass {{false}}. The value {{false}} indicates that the corresponding task will be canceled if it has not already started.
{mc}
Similarly, when attempting to cancel individual tasks within the thread pool using the {{Future.cancel()}} method, ensure that the tasks support interruption. If they do, pass a {{boolean}} argument {{true}} to {{cancel()}}, otherwise pass {{false}}. The value {{false}} indicates that the corresponding task will be canceled if it has not already started.
h2. Exceptions
*EX1*: Tasks that execute quickly and do not block may violate this guideline.
h2. Risk Assessment
Submitting tasks that are not interruptible may preclude the shut down procedure of a thread pool and cause denial of service.
|| Rule || Severity || Likelihood || Remediation Cost || Priority || Level ||
| CON36- J | low | probable | medium | {color:green}{*}P4{*}{color} | {color:green}{*}L3{*}{color} |
h3. Automated Detection
TODO
h3. Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+CON35-J].
h2. References
\[[API 06|AA. Java References#API 06]\] interface ExecutorService
\[[Goetz 06|AA. Java References#Goetz 06]\] Chapter 7: Cancellation and shutdown
----
[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_left.png!|CON12CON34-J. AvoidDo deadlocknot byuse requestingan andinstance releasinglock locksto inprotect theshared samestatic orderdata] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_up.png!|11. Concurrency (CON)] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_right.png!|VOID CON14CON37-J. Ensure atomicity of 64-bit operations] that tasks executing in a thread pool do not fail silently]
|