Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

External programs can be invoked from Java code using the exec() method of the java.lang.Runtime class. The exec() method returns an object of a subclass of the abstract class java.lang.Process. The exitValue() method can be used to observe the return value of the process. This recommendation discusses several issues resulting from the improper use of the exec() method.

...

This noncompliant code example invokes notemaker, a hypothetical cross-platform notepad application. However, the The program does not wait for the notemaker process to terminate, and throws an IllegalThreadStateException if the notemaker process has not completed when the exitValue() method is called.

...

In this noncompliant code example, the waitFor() method blocks the calling thread until the invoked process terminates. The code can be changed as shown in the modified noncompliant example below. This however, also has a shortcoming; the program may be remain blocked for a long time because of the limited buffer size used available for the standard output streams on many platforms. The output from the external prgram may exhaust the buffer causing this condition.

Code Block
bgColor#FFcccc
public class Exec {
  public static void main(String args[]) {
    try {
      Runtime rt = Runtime.getRuntime();
      Process proc = rt.exec("notemaker");
      int exitVal = proc.waitFor();
    } catch (Throwable t) { t.printStackTrace();}
  }
}

...

An inefficient solution is to exhaust the output (or stderr) stream and the stderr streams before beginning to wait for the process. A better option is to empty both the stderr and output streams. The code below shows this but is not the best solution since as it does not process any arguments passed to the external program (notemaker) and in turn exits with an OS-specific non-zero exit code.

Code Block
bgColor#ccccff
public class Exec {
  public static void main(String args[]) {
    try {
      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();
    } catch (Throwable t) { t.printStackTrace(); }
  }
}

Compliant Solution (2)

The second This compliant solution spawns a command interpreter and executes the user supplied command. It uses a separate OutputStream to write the output that is read in from the external process.

Code Block
bgColor#ccccff
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) { ioe.printStackTrace(); /* 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) { t.printStackTrace();/* forward to handler */ }
  }
}

Risk Assessment

Misuse of the exec() method can lead to result in runtime exceptions and denial of service vulnerabilities.

...