...
In this noncompliant code example, the waitFor()
method blocks the calling thread until the invoked process terminates. This prevents the {IllegalThreadStateException
}} seen in the previous example. However, the example program may experience an arbitrary delay before termination for any of the following reasons. First, the invoked notemaker process could legitimately require lengthy execution before completion. Although this possibility can present difficulties in practice, it is irrelevant for the purposes of this guideline. SecondlySecond, output from the notemaker process can exhaust the available buffer for the standard output or standard error stream. When this occurs, it can block the notemaker process as well, preventing all forward progress for both processes. Note that many platforms limit the buffer size available for the standard output streams.
Code Block | ||
---|---|---|
| ||
public class Exec { public static void main(String args[]) throws IOException { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); int exitVal = proc.waitFor(); } } |
Compliant Solution (1)
An inefficient partial solution is would be to exhaust the output and the stderr
streams before beginning to wait waiting for the invoked process. A better option approach is to empty both the stderr
stream and output streams. This compliant solution shows this but is not the best solution as it does not the output stream, as shown in this compliant solution. The example code, however, fails to process any arguments passed to the external program (notemaker
) and exits with an OS-specific non-zero exit code. These limitations must also be addressed in production code.
Code Block | ||
---|---|---|
| ||
public class Exec { public static void main(String args[]) throws IOException, InterruptedException { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); InputStream is = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { System.out.println(line); // Prints the error lines } int exitVal = proc.waitFor(); } } |
...
This compliant solution spawns a command interpreter and that executes the user supplied command. It uses a separate OutputStream
to write reads the output that is read in from the external process, and write it to a separate OutputStream
.
Code Block | ||
---|---|---|
| ||
class Exec extends Thread { InputStream is; String type; OutputStream os; Exec(InputStream is, String type) { this(is, type, null); } Exec(InputStream is, String type, OutputStream redirect) { this.is = is; this.type = type; this.os = redirect; } public void run() { try { PrintWriter pw = null; if (os != null) { pw = new PrintWriter(os); } InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { if (pw != null) { pw.println(line); pw.flush(); } System.out.println(type + ">" + line); } if (pw != null) { pw.flush(); } } catch (IOException ioe) { /* Forward to handler */ } } } public class ExecMe { public static void main(String[] args) { // ... perform command argument check ... try { FileOutputStream fos = new FileOutputStream("c:\\output.txt"); Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("notemaker"); // Any error message? Exec errorGobbler = new Exec(proc.getErrorStream(), "ERROR"); // Any output? Exec outputGobbler = new Exec(proc.getInputStream(), "OUTPUT", fos); errorGobbler.start(); outputGobbler.start(); // Any error? int exitVal = proc.waitFor(); errorGobbler.join(); // Handle condition where the outputGobbler.join(); // process ends before the threads finish fos.flush(); fos.close(); } catch (Throwable t) { /* forward to handler */ } } } |
Note that if the streams are not merged (as shown in the next compliant solution)When the output and error streams are handled separately, they must be drained concurrently. Failure to do this so can cause the program to block indefinitely.
...
This compliant solution (based on the Sun forums query Runtime.exec hangs even If I drain output), uses the a ProcessBuilder
to merge simplify the handling mechanism by merging the error and output streams to simplify the handling mechanism. The readToPrompt()
method interacts with the command prompt and reads the output of the invoked process; details of its implementation necessarily depend on the exact formatting of the command prompt used by the platform's command interpreter.
Code Block | ||
---|---|---|
| ||
public class Cmd { public static void main(String[] args) throws IOException { ProcessBuilder pb = new ProcessBuilder("cmd"); pb = pb.redirectErrorStream(true); Process p = pb.start(); InputStream is = p.getInputStream(); OutputStream os = p.getOutputStream(); PrintWriter pw = new PrintWriter(os, true); readToPrompt(is); pw.println("dir"); readToPrompt(is); } private static void readToPrompt(InputStream is) throws IOException { String s = ""; for (;;) { int i = is.read(); if (i < 0) { System.out.println(); System.out.println("EOF"); System.exit(0); } char c = (char)i; // Safe s += c; if (s.endsWith("\r\n") { System.out.print(s); s = ""; } // Detects prompt, to break out if (c == '>' && s.length() > 2 && s.charAt(1) == ':') { System.out.print(s); break; } } } } |
...
Misuse of the exec()
method can result in runtime exceptions and in denial of service vulnerabilities.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
FIO10-J | low | probable | medium | P4 | L3 |
Automated Detection
...
Related Vulnerabilities
...