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

Compare with Current View Page History

« Previous Version 28 Next »

Objects in general should — and security critical objects must — be left in a consistent state even when exceptional conditions arise. Common techniques for maintaining object consistency include

  • Input validation (for example, method parameters)
  • Reordering logic so that code that can result in the exceptional condition executes before the object is modified
  • Using rollbacks in the event of failure
  • Performing required operations on a temporary copy of the object and committing changes to the original object only after their successful completion
  • Avoiding the need to modify the object in the first place

Noncompliant Code Example

This noncompliant code example shows a Dimensions class that contains three internal attributes, the length, width and height of a rectangular box. The getVolumePackage() method is designed to return the total volume required to hold the box, after accounting for packaging material which further adds 2 units to the dimensions of each side. Non positive values of the dimensions of the box (exclusive of packaging material) are rejected during input validation. Also, the weight of the object is passed in as an argument and cannot be more than 20 units. Consider the case where the weight is more than 20 units (21 units, here). This causes an IllegalArgumentException which is intercepted by the custom error reporter. While the logic restores the object's original state in the absence of this exception, the rollback code fails to execute in the event of an exception. Consequently, subsequent invocations of getVolumePackage() produce incorrect results.

class Dimensions {
  private int length;
  private int width;
  private int height;

  public Dimensions(int length, int width, int height) {
    this.length = length;
    this.width = width;
    this.height = height;
  }

  protected int getVolumePackage(int weight) {
    length += 2;
    width  += 2;
    height += 2;
    try {
      if(length <= 2 || width <= 2 || height <= 2 || weight <= 0 || weight > 20)
        throw new IllegalArgumentException();
	
        int volume = length * width * height; // 12 * 12 * 12 = 1728
        length -=2; width -= 2; height -= 2; // Revert back
        return volume;
      } catch(Throwable t) { 
        MyExceptionReporter mer = new MyExceptionReporter();
        mer.report(t); // Sanitize 
        return -1; // Non-positive error code
      }	
  }

  public static void main(String[] args) {
    Dimensions d = new Dimensions(10, 10, 10);
    System.out.println(d.getVolumePackage(21)); // Prints -1 (error)
    System.out.println(d.getVolumePackage(19)); // Prints 2744 instead of 1728
  }
}

Note that the explicitly thrown exception in this example is a stand-in for an exception that propagates from larger and more complex code in a real program. Consequently, this example should be considered purely notional.

Compliant Solution (Roll back)

This compliant solution restores prior object state in the event of an exception.

// ...
} catch(Throwable t) { 
  MyExceptionReporter mer = new MyExceptionReporter();
  mer.report(t); // Sanitize 
  length -=2; width -= 2; height -= 2; // Revert back
  return -1;
}	

Compliant Solution (Input validation)

This improved compliant solution performs input validation before modifying the state of the object. Note that the try block contains only those statements that could throw the exception; all others have been moved outside the try block.

protected int getVolumePackage(int weight) {
  try {
    if(length <= 0 || width <= 0 || height <= 0 || weight <= 0 || weight > 20)
      throw new IllegalArgumentException(); // Validate first
  } catch(Throwable t) { MyExceptionReporter mer = new MyExceptionReporter();
    mer.report(t); // Sanitize 
    return -1;
  }		

  length += 2;
  width  += 2;
  height += 2;

  int volume = length * width * height;
  length -=2; width -= 2; height -= 2;
  return volume;
}

Compliant Solution (Modification Avoided)

This compliant solution entirely avoids the need to modify the object; consequently object invariants cannot be violated and rollback is unnecessary. This approach is preferred over the other compliant solutions, when possible. Note, however, that this approach but may be infeasible for code more complicated than this notional example.

protected int getVolumePackage(int weight) {
  final static int packThickness = 2;
  try {
    if(length <= 0 || width <= 0 || height <= 0 || weight <= 0 || weight > 20)
      throw new IllegalArgumentException(); // Validate first
  } catch(Throwable t) { MyExceptionReporter mer = new MyExceptionReporter();
    mer.report(t); // Sanitize 
    return -1;
  }		


  int volume = (length + packThickness) * (width + packThickness) * (height + packThickness);
  return volume;
}

Risk Assessment

Failure to restore prior object state on method failure can leave the object in an inconsistent state and can violate required state invariants.

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

EXC11-J

low

probable

high

P2

L3

Related Vulnerabilities

CVE-2008-0002

Bibliography

[[Bloch 2008]] Item 64: Strive for failure atomicity


EXC10-J. Do not let code throw undeclared checked exceptions      06. Exceptional Behavior (EXC)      EXC12-J. Do not log unsanitized user input

  • No labels