Recommendations discussed The recommendations suggested in the guideline CON13-J. Ensure that threads are stopped cleanly are insufficient to terminate a thread that is blocked on a blocking operation such as network or file input-output (IO). Consequently, threads and tasks should provide callers with an explicit termination mechanism to prevent denial of service vulnerabilities.
...
This noncompliant code example is similar to the preceding one but uses thread interruption to indicate that it is safe to shut down the thread. However, this is not useful because the thread is blocked on network IO as a consequence of using the readLine()
method. Network IO is not responsive to thread interruption whne when a java.net.Socket
is being used.
...
Compliant Solution (close socket connection)
This compliant solution closes resumes the socket connectionthread, by having the shutdown()
method close the socket. As a result, the thread is bound to stop because the readLine()
method will throw a SocketException
when the socet is closed. Note that there is no way to keep the connection alive if the thread is to be cleanly halted immediately.
Code Block | ||
---|---|---|
| ||
public final class SocketReader implements Runnable { // ... public void readData() throws IOException { String string; try { while ((string = in.readLine()) != null) { // Blocks until end of stream (null) } } finally { shutdown(); } } public void shutdown() throws IOException { socket.close(); } public static void main(String[] args) throws IOException, InterruptedException { SocketReader reader = new SocketReader(); Thread thread = new Thread(reader); thread.start(); Thread.sleep(1000); reader.shutdown(); } } |
The finally
block executes after shutdown()
is called. Because the finally
block also calls the shutdown()
method, it is possible that the socket will be closed a second time, which has no effect.
A boolean
flag can be used (as described earlier) if additional clean-up operations need to be performed. When performing asynchronous IO, a java.nio.channels.Selector
may also be brought out of the blocked state by either invoking its close()
or wakeup()
method.
A boolean
flag can be used (as shown earlier) if additional operations need to be performed. When supplementing the code with a flag, the shutdown()
method should also set the flag to false so that the thread can cleanly exit from the while loop.
Compliant Solution (interruptible channel)
...
Code Block | ||
---|---|---|
| ||
public final class SocketReader implements Runnable { private final SocketChannel sc; private final Object lock = new Object(); public SocketReader(String host, int port) throws IOException { sc = SocketChannel.open(new InetSocketAddress(host, port)); } public void run() { ByteBuffer buf = ByteBuffer.allocate(1024); try { synchronized (lock) { while (!Thread.interrupted()) { sc.read(buf); // ... } } } catch (IOException ie) { // Forward to handler } } public static void main(String[] args) throws IOException, InterruptedException { SocketReader reader = new SocketReader("somehost", 25); Thread thread = new Thread(reader); thread.start(); Thread.sleep(1000); thread.interrupt(); } } |
This method technique interrupts the current thread, however, it only stops the thread because the code polls the interrupted flag using the method Thread.interrupted()
, and shuts down the thread when it is interrupted. Using a SocketChannel
ensures that the condition in the while loop is tested as soon as an interruption is received, despite the read operation being a blocking operation. Invoking the interrupt()
method of a thread that is blocked because of a java.nio.channels.Selector
also causes the thread to awaken.
...