Lack of concern about leaving objects in an inconsistent state when exceptional conditions arise may leave them vulnerable. Usual techniques for avoiding this scenario include:
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 adds a further 2 units to the dimensions of each side. Non positive values are rejected during the input validation. Also, the weight of the object is passed in as a parameter 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, it omits doing the same from within the catch block. This violates the object's invariants such that when getVolumePackage() is called for the second time, it produces 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 <= 0 || width <= 0 || height <= 0 || 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
}
}
|
To be compliant, restore prior object state on exceptional conditions.
// ...
} catch(Throwable t) {
MyExceptionReporter mer = new MyExceptionReporter();
mer.report(t); // sanitize
length -=2; width -= 2; height -= 2; // revert back
return -1;
}
|
A more preferable way is to perform input validation before modifying the state of the object.
protected int getVolumePackage(int weight) {
try {
if(length <= 0 || width <= 0 || height <= 0 || weight <= 0 || weight > 20)
throw new IllegalArgumentException(); // validate first
length += 2;
width += 2;
height += 2;
int volume = length * width * height;
length -=2; width -= 2; height -= 2;
return volume;
} catch(Throwable t) { MyExceptionReporter mer = new MyExceptionReporter();
mer.report(t); // sanitize
return -1;
}
}
|
Failing to restore prior object state on method failure can leave the object in an inconsistent state.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
|---|---|---|---|---|---|
EXC07- J |
low |
probable |
high |
P2 |
L3 |
TODO
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 64: Strive for failure atomicity |
EXC06-J. Do not let code throw undeclared checked exceptions 12. Exceptional Behavior (EXC) EXC30-J. Do not exit abruptly from a finally block