You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 15 Next »

External programs can be invoked from Java code using the exec() method of java.lang.Runtime class. As a result, a reference to the Process class is returned to the JVM. The exitValue() method can be used to observe the return value of the process. There are several nuances of Rutime.exec() that are discussed in this recommendation.

Noncompliant Code Example

This noncompliant code example calls notemaker, a hypothetical cross-platform notepad application. Despite compiling successfully, it throws an IllegalThreadStateException if the notemaker process has not completed.

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

By using the waitFor() method for blocking, this code can be modified as shown in the modified noncompliant example below. This also has a shortcoming: the program hangs due to the limited buffer size used for standard output streams on many platforms. This is the result of failure in reading/writing to the streams in a timely fashion.

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

Compliant Solution

An inefficient solution would be to exhaust the output (or stderr) stream 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 it does not process any arguments passed to the external program (notemaker) and in turn exits with an OS-specific non-zero exit code.

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;

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

The second 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.

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.FileOutputStream;

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

Risk Assessment

Misusing exec() can seriously compromise the security of a Java application.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

FIO02-J

medium

unlikely

medium

P4

L3

Automated Detection

TODO

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

[[API 06]] exec()
[[Daconta 00]]
[[Daconta 03]] Pitfall 1


FIO01-J. Canonicalize path names originating from untrusted sources      07. Input Output (FIO)      FIO38-J. Validate user input

  • No labels