Reliability - Java coding guidelines: 75 recommendations for reliable and secure programs (2014)

Java coding guidelines: 75 recommendations for reliable and secure programs (2014)

Chapter 3. Reliability

ISO/IEC/IEEE 24765:2010, Systems and software engineering—Vocabulary, defines reliability as the ability of a system or component to perform its required functions under stated conditions for a specified period of time [ISO/IEC/IEEE 24765:2010]. ISO/IEC 9126-1:2001, Software engineering—Product quality—Part 1: Quality model, provides a similar definition of reliability as the capability of the software product to maintain a specified level of performance when used under specified conditions [ISO/IEC 9126-1:2001].

Software reliability is an important factor affecting system reliability. It differs from hardware reliability in that it reflects design perfection, rather than manufacturing perfection. Wear or aging does not occur in software. Limitations in reliability are the results of faults in requirements, design, and implementation. Failures resulting from these faults depend on the way the software product is used and the program options selected rather than on elapsed time.

ISO/IEC/IEEE 24765:2010 defines software reliability as the probability that software will not cause the failure of a system for a specified time under specified conditions [ISO/IEC/IEEE 24765:2010]. The probability is a function not only of the inputs to and use of the system, but also of the existence of faults in the software. The inputs to the system determine whether existing faults, if any, are encountered. The high complexity of software is the major contributing factor to software reliability problems.

These guidelines deal with Java language features that can easily be misused by the unwary. The Java language allows a great deal of flexibility in the ways in which it is used, but some of these uses can lead to obscure techniques and code that is difficult to understand and maintain. By following these guidelines, programmers will produce code that is less prone to bugs and runtime failure.

This chapter includes guidelines that

1. Help reduce errors, and are consequently important for developing reliable Java code

2. Contain specific Java coding recommendations to improve software reliability

37. Do not shadow or obscure identifiers in subscopes

Reuse of identifier names in subscopes leads to obscuration or shadowing. Reused identifiers in the current scope can render those defined elsewhere inaccessible. Although the Java Language Specification (JLS) [JLS 2013] clearly resolves any syntactic ambiguity arising from obscuring or shadowing, such ambiguity burdens source code maintainers and auditors, especially when code requires access to both the original named entity and the inaccessible one. The problem is exacerbated when the reused name is defined in a different package.

According to §6.4.2, “Obscuring,” of the JLS [JLS 2013],

A simple name may occur in contexts where it may potentially be interpreted as the name of a variable, a type, or a package. In these situations, the rules of §6.5 specify that a variable will be chosen in preference to a type, and that a type will be chosen in preference to a package.

This implies that a variable can obscure a type or a package, and a type can obscure a package name. Shadowing, on the other hand, refers to one variable rendering another variable inaccessible in a containing scope. One type can also shadow another type.

No identifier should obscure or shadow another identifier in a containing scope. For example, a local variable should not reuse the name of a class field or method or a class name or package name. Similarly, an inner class name should not reuse the name of an outer class or package.

Both overriding and shadowing differ from hiding, in which an accessible member (typically non-private) that should have been inherited by a subclass is replaced by a locally declared subclass member that assumes the same name but has a different, incompatible method signature.

Noncompliant Code Example (Field Shadowing)

This noncompliant code example reuses the name of the val instance field in the scope of an instance method.


class MyVector {
private int val = 1;
private void doLogic() {
int val;
//...
}
}


The resulting behavior can be classified as shadowing; the method variable renders the class variable inaccessible within the scope of the method. For example, assigning to this.val from within the method does not affect the value of the class variable.

Compliant Solution (Field Shadowing)

This compliant solution eliminates shadowing by changing the name of the variable defined in the method scope from val to newValue:


class MyVector {
private int val = 1;
private void doLogic() {
int newValue;
//...
}
}


Noncompliant Code Example (Variable Shadowing)

This example is noncompliant because the variable i defined in the scope of the second for loop block shadows the definition of the instance variable i defined in the MyVector class:


class MyVector {
private int i = 0;
private void doLogic() {
for (i = 0; i < 10; i++) {/* ... */}
for (int i = 0; i < 20; i++) {/* ... */}
}
}


Compliant Solution (Variable Shadowing)

In this compliant solution, the loop counter i is only defined in the scope of each for loop block:


class MyVector {
private void doLogic() {
for (int i = 0; i < 10; i++) {/* ... */}
for (int i = 0; i < 20; i++) {/* ... */}
}
}


Applicability

Name reuse makes code more difficult to read and maintain, which can result in security weaknesses. An automated tool can easily detect the reuse of identifiers in containing scopes.

Bibliography

[Bloch 2005]

Puzzle 67, “All Strung Out”

[Bloch 2008]

Item 16, “Prefer Interfaces to Abstract Classes”

[Conventions 2009]

§6.3, “Placement”

[FindBugs 2008]

DLS, “Dead store to local variable that shadows field”

[JLS 2013]

§6.4.1, “Shadowing”

§6.4.2, “Obscuring”

§7.5.2, “Type-Import-on-Demand Declarations”

38. Do not declare more than one variable per declaration

Declaring multiple variables in a single declaration could cause confusion about the types of variables and their initial values. In particular, do not declare any of the following in a single declaration:

Image Variables of different types

Image A mixture of initialized and uninitialized variables

In general, you should declare each variable on its own line with an explanatory comment regarding its role. While not required for conformance with this guideline, this practice is also recommended in the Code Conventions for the Java Programming Language, §6.1, “Number Per Line” [Conventions 2009].

This guideline applies to

Image Local variable declaration statements [JLS 2013, §14.4]

Image Field declarations [JLS 2013, §8.3]

Image Field (constant) declarations [JLS 2013, §9.3]

Noncompliant Code Example (Initialization)

This noncompliant code example might lead a programmer or reviewer to mistakenly believe that both i and j are initialized to 1. In fact, only j is initialized, while i remains uninitialized:

int i, j = 1;

Compliant Solution (Initialization)

In this compliant solution, it is readily apparent that both i and j are initialized to 1:


int i = 1; // Purpose of i...
int j = 1; // Purpose of j...


Compliant Solution (Initialization)

In this compliant solution, it is readily apparent that both i and j are initialized to 1:

int i = 1, j = 1;

Declaring each variable on a separate line is the preferred method. However, multiple variables on one line are acceptable when they are trivial temporary variables such as array indices.

Noncompliant Code Example (Different Types)

In this noncompliant code example, the programmer declares multiple variables, including an array, on the same line. All instances of the type T have access to methods of the Object class. However, it is easy to forget that arrays require special treatment when some of these methods are overridden.


public class Example<T> {
private T a, b, c[], d;

public Example(T in) {
a = in;
b = in;
c = (T[]) new Object[10];
d = in;
}
}


When an Object method, such as toString(), is overridden, a programmer could accidentally provide an implementation for type T that fails to consider that c is an array of T rather than a reference to an object of type T.


public String toString() {
return a.toString() + b.toString() +
c.toString() + d.toString();
}


However, the programmer’s intent could have been to invoke toString() on each individual element of the array c.


// Correct functional implementation
public String toString() {
String s = a.toString() + b.toString();
for (int i = 0; i < c.length; i++){
s += c[i].toString();
}
s += d.toString();
return s;
}


Compliant Solution (Different Types)

This compliant solution places each declaration on its own line and uses the preferred notation for array declaration:


public class Example<T> {
private T a; // Purpose of a...
private T b; // Purpose of b...
private T[] c; // Purpose of c[]...
private T d; // Purpose of d...
public Example(T in){
a = in;
b = in;
c = (T[]) new Object[10];
d = in;
}
}


Applicability

Declaration of multiple variables per line can reduce code readability and lead to programmer confusion.

When more than one variable is declared in a single declaration, ensure that both the type and the initial value of each variable are self-evident.

Declarations of loop indices should be included within a for statement, even when this results in variable declarations that lack a comment about the purpose of the variable:


public class Example {
void function() {
int mx = 100; // Some max value

for (int i = 0; i < mx; ++i ) {
/* ... */
}

}
}


Such declarations are not required to be on a separate line, and the explanatory comment may be omitted.

Bibliography

[Conventions 2009]

§6.1, “Number Per Line”

[ESA 2005]

Rule 9, Put Single Variable Definitions in Separate Lines

[JLS 2013]

§4.3.2, “The class Object”

§6.1, “Declarations”

§8.3, “Field Declarations”

§9.3, “Field (Constant) Declarations”

§14.4, “Local Variable Declaration Statements”

39. Use meaningful symbolic constants to represent literal values in program logic

Java supports the use of various types of literals, such as integers (5, 2), floating-point numbers (2.5, 6.022e+23), characters ('a', '\n'), Booleans (true, false), and strings ("Hello\n"). Extensive use of literals in a program can lead to two problems. First, the meaning of the literal is often obscured or unclear from the context. Second, changing a frequently used literal requires searching the entire program source for that literal and distinguishing the uses that must be modified from those that should remain unmodified.

Avoid these problems by declaring class variables with meaningfully named constants, setting their values to the desired literals, and referencing the constants instead of the literals throughout the program. This approach clearly indicates the meaning or intended use of each literal. Furthermore, should the constant require modification, the change is limited to the declaration; searching the code is unnecessary.

Constants should be declared as static and final. However, constants should not be declared public and final if their values might change (see Guideline 31, “Do not apply public final to constants whose value might change in later releases,” for more details). For example,

private static final int SIZE = 25;

Although final can be used to specify immutable constants, there is a caveat when dealing with composite objects. See Guideline 73, “Never confuse the immutability of a reference with that of the referenced object,” for more details.

Noncompliant Code Example

This noncompliant code example calculates approximate dimensions of a sphere, given its radius:


double area(double radius) {
return 3.14 * radius * radius;
}

double volume(double radius) {
return 4.19 * radius * radius * radius;
}

double greatCircleCircumference(double radius) {
return 6.28 * radius;
}


The methods use the seemingly arbitrary literals 3.14, 4.19, and 6.28 to represent various scaling factors used to calculate these dimensions. A developer or maintainer reading this code would have little idea about how they were generated or what they mean and consequently would not understand the function of this code.

Noncompliant Code Example

This noncompliant code example attempts to avoid the problem by explicitly calculating the required constants:


double area(double radius) {
return 3.14 * radius * radius;
}

double volume(double radius) {
return 4.0 / 3.0 * 3.14 * radius * radius * radius;
}

double greatCircleCircumference(double radius) {
return 2 * 3.14 * radius;
}


The code uses the literal 3.14 to represent the value π. Although it removes some of the ambiguity from the literals, it complicates code maintenance. If the programmer were to decide that a more precise value of π is desired, all occurrences of 3.14 in the code would have to be found and replaced.

Compliant Solution (Constants)

In this compliant solution, a constant PI is declared and initialized to 3.14. Thereafter, it is referenced in the code whenever the value of π is needed.


private static final double PI = 3.14;

double area(double radius) {
return PI * radius * radius;
}

double volume(double radius) {
return 4.0/3.0 * PI * radius * radius * radius;
}

double greatCircleCircumference(double radius) {
return 2 * PI * radius;
}


This technique reduces clutter and promotes maintainability. If a more precise approximation of the value of π is required, the programmer can simply redefine the constant. The use of the literals 4.0, 3.0, and 2 does not violate this guideline, for reasons explained in the “Applicability” section of this guideline.

Compliant Solution (Predefined Constants)

Use predefined constants when they are available. The class java.lang.Math defines a large group of numeric constants, including PI and the exponential constant E.


double area(double radius) {
return Math.PI * radius * radius;
}

double volume(double radius) {
return 4.0/3.0 * Math.PI * radius * radius * radius;
}

double greatCircleCircumference(double radius) {
return 2 * Math.PI * radius;
}


Noncompliant Code Example

This noncompliant code example defines a constant BUFSIZE, but then defeats the purpose of defining BUFSIZE as a constant by assuming a specific value for BUFSIZE in the following expression:


private static final int BUFSIZE = 512;

// ...

public void shiftBlock() {
int nblocks = 1 + ((nbytes - 1) >> 9); // BUFSIZE = 512 = 2^9
// ...
}


The programmer has assumed that BUFSIZE is 512, and right-shifting 9 bits is the same (for positive numbers) as dividing by 512. However, if BUFSIZE changes to 1024 in the future, modifications will be difficult and error prone.

This code also fails to conform to The CERT® Oracle® Secure Coding Standard for Java [Long 2012], “NUM01-J. Do not perform bitwise and arithmetic operations on the same data.” Replacing a division operation with a right shift is considered a premature optimization. Normally, the compiler will do a better job of determining when this optimization should be performed.

Compliant Solution

This compliant solution uses the identifier assigned to the constant value in the expression:


private static final int BUFSIZE = 512;

// ...

public void shiftBlock(int nbytes) {
int nblocks = 1 + (nbytes - 1) / BUFSIZE;
// ...
}


Applicability

Using numeric literals makes code more difficult to read, understand, and edit.

The use of symbolic constants should be restricted to cases in which they improve the readability and maintainability of the code. When the intent of the literal is obvious, or where the literal is not likely to change, using symbolic constants can impair code readability. The following code example obscures the meaning of the code by using too many symbolic constants.


private static final double FOUR = 4.0;
private static final double THREE = 3.0;

double volume(double radius) {
return FOUR / THREE * Math.PI * radius * radius * radius;
}


The values 4.0 and 3.0 in the volume calculation are clearly scaling factors used to calculate the sphere’s volume and are not subject to change (unlike the approximate value for π), so they can be represented exactly. There is no reason to change them to increase precision because replacing them with symbolic constants actually impairs the readability of the code.

Bibliography

[Core Java 2003]

[Long 2012]

NUM01-J. Do not perform bitwise and arithmetic operations on the same data

40. Properly encode relationships in constant definitions

The definitions of constant expressions should be related exactly when the values they express are also related.

Noncompliant Code Example

In this noncompliant code example, OUT_STR_LEN must always be exactly two greater than IN_STR_LEN. These definitions fail to reflect this requirement:


public static final int IN_STR_LEN = 18;
public static final int OUT_STR_LEN = 20;


Compliant Solution

In this compliant solution, the relationship between the two values is represented in the definitions:


public static final int IN_STR_LEN = 18;
public static final int OUT_STR_LEN = IN_STR_LEN + 2;


Noncompliant Code Example

In this noncompliant code example, there appears to be an underlying relationship between the two constants where none exists:


public static final int VOTING_AGE = 18;
public static final int ALCOHOL_AGE = VOTING_AGE + 3;


A programmer performing routine maintenance may modify the definition for VOTING_AGE, but fail to recognize the resulting change in the definition for ALCOHOL_AGE.

Compliant Solution

In this compliant solution, the definitions reflect the independence of the two constants:


public static final int VOTING_AGE = 18;
public static final int ALCOHOL_AGE = 21;


Bibliography

[JLS 2013]

§4.12.4, “final Variables”

41. Return an empty array or collection instead of a null value for methods that return an array or collection

Some APIs intentionally return a null reference to indicate that instances are unavailable. This practice can lead to denial-of-service vulnerabilities when the client code fails to explicitly handle the null return value case. A null return value is an example of an in-band error indicator, which is discouraged by Guideline 52, “Avoid in-band error indicators.” For methods that return a set of values using an array or collection, returning an empty array or collection is an excellent alternative to returning a null value, as most callers are better equipped to handle an empty set than a null value.

Noncompliant Code Example

This noncompliant code example returns a null ArrayList when the size of the Array-List is 0. The class Inventory contains a getStock() method that constructs a list of items that have 0 inventory and returns the list of items to the caller.


class Inventory {
private final Hashtable<String, Integer> items;
public Inventory() {
items = new Hashtable<String, Integer>();
}

public List<String> getStock() {
List<String> stock = new ArrayList<String>();
Enumeration itemKeys = items.keys();
while (itemKeys.hasMoreElements()) {
Object value = itemKeys.nextElement();
if ((items.get(value)) == 0) {
stock.add((String)value);
}
}

if (items.size() == 0) {
return null;
} else {
return stock;
}
}
}

public class Client {
public static void main(String[] args) {
Inventory inv = new Inventory();
List<String> items = inv.getStock();
System.out.println(items.size());
}
}


When the size of this list is 0, a null value is returned with the assumption that the client will install the necessary checks. In this code example, the client lacks any null value check, causing a NullPointerException at runtime.

Compliant Solution

Instead of returning a null value, this compliant solution simply returns the List, even when it is empty.


class Inventory {
private final Hashtable<String, Integer> items;
public Inventory() {
items = new Hashtable<String, Integer>();
}

public List<String> getStock() {
List<String> stock = new ArrayList<String>();
Integer noOfItems; // Number of items left in the inventory
Enumeration itemKeys = items.keys();
while (itemKeys.hasMoreElements()) {
Object value = itemKeys.nextElement();

if ((noOfItems = items.get(value)) == 0) {
stock.add((String)value);
}
}
return stock; // Return list (possibly zero-length)
}
}

public class Client {
public static void main(String[] args) {
Inventory inv = new Inventory();
List<String> items = inv.getStock();
System.out.println(items.size());
}
}


The client can handle this situation effectively without being interrupted by runtime exceptions. When returning arrays rather than collections, ensure that the client avoids attempts to access individual elements of a zero-length array. This prevents an ArrayIndexOutOfBoundsException from being thrown.

Compliant Solution

This compliant solution returns an explicit empty list, which is an equivalent permissible technique.


public List<String> getStock() {
List<String> stock = new ArrayList<String>();
Integer noOfItems; // Number of items left in the inventory
Enumeration itemKeys = items.keys();
while (itemKeys.hasMoreElements()) {
Object value = itemKeys.nextElement();

if ((noOfItems = items.get(value)) == 0) {
stock.add((String)value);
}
}

if (l.isEmpty()) {
return Collections.EMPTY_LIST; // Always zero-length
} else {
return stock; // Return list
}
}

// Class Client ...


Applicability

Returning a null value rather than a zero-length array or collection may lead to denial-of-service vulnerabilities when the client code fails to handle null return values properly.

Automatic detection is straightforward; fixing the problem typically requires programmer intervention.

Bibliography

[Bloch 2008]

Item 43, “Return Empty Arrays or Collections, Not Nulls”

42. Use exceptions only for exceptional conditions

Exceptions should be used only to denote exceptional conditions; they should not be used for ordinary control flow purposes. Catching a generic object such as Throwable is likely to catch unexpected errors; see The CERT® Oracle® Secure Coding Standard for Java [Long 2012] ERR08-J, “Do not catch NullPointerException or any of its ancestors,” for examples. When a program catches a specific type of exception, it does not always know from where that exception was thrown. Using a catch clause to handle an exception that occurs in a distant known location is a poor solution; it is preferable to handle the error as soon as it occurs or to prevent it, if possible.

The nonlocality of throw statements and corresponding catch statements can also impede optimizers from improving code that relies on exception handling. Relying on catching exceptions for control flow also complicates debugging, because exceptions indicate a jump in control flow from the throw statement to the catch clause. Finally, exceptions need not be highly optimized as it is assumed that they are thrown only in exceptional circumstances. Throwing and catching an exception frequently has worse performance than handling the error with some other mechanism.

Noncompliant Code Example

This noncompliant code example attempts to concatenate the processed elements of the strings array:


public String processSingleString(String string) {
// ...
return string;
}
public String processStrings(String[] strings) {
String result = "";
int i = 0;
try {
while (true) {
result = result.concat(processSingleString(strings[i]));
i++;
}
} catch (ArrayIndexOutOfBoundsException e) {
// Ignore, we're done
}
return result;
}


This code uses an ArrayIndexOutOfBoundsException to detect the end of the array. Unfortunately, because ArrayIndexOutOfBoundsException is a RuntimeException, it could be thrown by processSingleString() without being declared in a throws clause. So it is possible for processStrings() to terminate prematurely before processing all of the strings.

Compliant Solution

This compliant solution uses a standard for loop to concatenate the strings.


public String processStrings(String[] strings) {
String result = "";
for (int i = 0; i < strings.length; i++) {
result = result.concat(processSingleString(strings[i]));
}
return result;
}


This code need not catch ArrayIndexOutOfBoundsException because it is a runtime exception, and such exceptions indicate programmer errors, which are best resolved by fixing the defect.

Applicability

Use of exceptions for any purpose other than detecting and handling exceptional conditions complicates program analysis and debugging, degrades performance, and can increase maintenance costs.

Bibliography

[Bloch 2001]

Item 39, “Use Exceptions Only for Exceptional Conditions”

[JLS 2013]

Chapter 11, “Exceptions”

[Long 2012]

ERR08-J. Do not catch NullPointerException or any of its ancestors

43. Use a try-with-resources statement to safely handle closeable resources

The Java Development Kit 1.7 (JDK 1.7) introduced the try-with-resources statement (see the JLS, §14.20.3, “try-with-resources” [JLS 2013]), which simplifies correct use of resources that implement the java.lang.AutoCloseable interface, including those that implement thejava.io.Closeable interface.

Using the try-with-resources statement prevents problems that can arise when closing resources with an ordinary try-catch-finally block, such as failing to close a resource because an exception is thrown as a result of closing another resource, or masking an important exception when a resource is closed.

Use of the try-with-resources statement is also illustrated in The CERT® Oracle® Secure Coding Standard for Java [Long 2012], “ERR05-J. Do not let checked exceptions escape from a finally block,” “FIO03-J. Remove temporary files before termination,” and “FIO04-J. Close resources when they are no longer needed.”

Noncompliant Code Example

This noncompliant code example uses an ordinary try-catch-finally block in an attempt to close two resources.


public void processFile(String inPath, String outPath)
throws IOException{
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(inPath));
bw = new BufferedWriter(new FileWriter(outPath));
// Process the input and produce the output
} finally {
try {
if (br != null) {
br.close();
}
if (bw != null) {
bw.close();
}
} catch (IOException x) {
// Handle error
}
}
}


However, if an exception is thrown when the BufferedReader br is closed, then the BufferedWriter bw will not be closed.

Compliant Solution (Second finally Block)

This compliant solution uses a second finally block to guarantee that bw is properly closed even when an exception is thrown while closing br.


public void processFile(String inPath, String outPath)
throws IOException {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(inPath));
bw = new BufferedWriter(new FileWriter(outPath));
// Process the input and produce the output
} finally {
if (br != null) {
try {
br.close();
} catch (IOException x) {
// Handle error
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException x) {
// Handle error
}
}
}
}
}
}


Compliant Solution (try-with-resources)

This compliant solution uses a try-with-resources statement to manage both br and bw:


public void processFile(String inPath, String outPath)
throws IOException{
try (BufferedReader br =
new BufferedReader(new FileReader(inPath));
BufferedWriter bw =
new BufferedWriter(new FileWriter(outPath));) {
// Process the input and produce the output
} catch (IOException ex) {
// Print out all exceptions, including suppressed ones
System.err.println("thrown exception: " + ex.toString());
Throwable[] suppressed = ex.getSuppressed();
for (int i = 0; i < suppressed.length; i++) {
System.err.println("suppressed exception: " +
suppressed[i].toString());
}
}
}


This solution preserves any exceptions thrown during the processing of the input while still guaranteeing that both br and bw are properly closed, regardless of what exceptions occur. Finally, this code demonstrates how to access every exception that may be produced from the try-with-resources block.

If only one exception is thrown, during opening, processing, or closing of the files, the exception will be printed after "thrown exception:". If an exception is thrown during processing, and a second exception is thrown while trying to close either file, the second exception will be printed after "thrown exception:", and the first exception will be printed after "suppressed exception:".

Applicability

Failing to correctly handle all failure cases when working with closeable resources may result in some resources not being closed or in important exceptions being masked, possibly resulting in a denial of service. Note that failure to use a try-with-resources statement cannot be considered a security vulnerability in and of itself, because it is possible to write a correctly structured group of nested try-catch-finally blocks guarding the resources that are in use (see “ERR05-J. Do not let checked exceptions escape from a finally block” [Long 2012]). That said, failure to correctly handle such error cases is a common source of vulnerabilities. Use of a try-with-resources statement mitigates this issue by guaranteeing that the resources are managed correctly and that exceptions are never masked.

Bibliography

[JLS 2013]

§14.20.3, “try-with-resources”

[Long 2012]

ERR05-J. Do not let checked exceptions escape from a finally block

FIO03-J. Remove temporary files before termination

FIO04-J. Close resources when they are no longer needed

[Tutorials 2013]

The try-with-resources Statement

44. Do not use assertions to verify the absence of runtime errors

Diagnostic tests can be incorporated into programs by using the assert statement. Assertions are primarily intended for use during debugging and are often turned off before code is deployed by using the -disableassertions (or -da) Java runtime switch. Consequently, assertions should be used to protect against incorrect programmer assumptions, and not for runtime error checking.

Assertions should never be used to verify the absence of runtime (as opposed to logic) errors, such as

Image Invalid user input (including command-line arguments and environment variables)

Image File errors (for example, errors opening, reading, or writing files)

Image Network errors (including network protocol errors)

Image Out-of-memory conditions (when the Java Virtual Machine cannot allocate space for a new object, and the garbage collector cannot make sufficient space available)

Image System resource exhaustion (for example, out-of-file descriptors, processes, threads)

Image System call errors (for example, errors executing files or locking or unlocking mutexes)

Image Invalid permissions (for example, file, memory, user)

Code that protects against an I/O error, for example, cannot be implemented as an assertion because it must be present in the deployed executable.

Assertions are generally unsuitable for server programs or embedded systems in deployment. A failed assertion can lead to a denial-of-service (DoS) attack if triggered by a malicious user. In such situations, a soft failure mode, such as writing to a log file, is more appropriate.

Noncompliant Code Example

This noncompliant code example uses the assert statement to verify that input was available:


BufferedReader br;

// Set up the BufferedReader br

String line;

// ...

line = br.readLine();

assert line != null;


Because input availability depends on the user and can be exhausted at any point during program execution, a robust program must be prepared to gracefully handle and recover from the unavailability of input. However, using the assert statement to verify that some significant input was available is inappropriate because it might lead to an abrupt termination of the process, resulting in a denial of service.

Compliant Solution

This compliant solution demonstrates the recommended way to detect and handle unavailability of input:


BufferedReader br;

// Set up the BufferedReader br

String line;

// ...

line = br.readLine();

if (line == null) {
// Handle error
}


Applicability

Assertions are a valuable diagnostic tool for finding and eliminating software defects that may result in vulnerabilities. The absence of assertions, however, does not mean that code is bug-free.

In general, the misuse of the assert statement for runtime checking rather than checking for logical errors cannot be detected automatically.

Bibliography

[JLS 2013]

§14.10, “The assert Statement”

45. Use the same type for the second and third operands in conditional expressions

The conditional operator ?: uses the boolean value of its first operand to decide which of the other two expressions will be evaluated. (See §15.25, “Conditional Operator ? :,” of the JLS [JLS 2013].)

The general form of a Java conditional expression is operand1 ? operand2 : operand3.

Image If the value of the first operand (operand1) is true, then the second operand expression (operand2) is chosen.

Image If the value of the first operand is false, then the third operand expression (operand3) is chosen.

The conditional operator is syntactically right-associative. For example, a?b:c?d:e?f:g is equivalent to a?b:(c?d:(e?f:g)).

The JLS rules for determining the result type of a conditional expression (see Table 3–1) are complicated; programmers could be surprised by the type conversions required for expressions they have written.

Image

Table 3–1. Determining the result type of a conditional expression

Result type determination begins from the top of the table; the compiler applies the first matching rule. The Operand 2 and Operand 3 columns refer to operand2 and operand3 (from the previous definition) respectively. In the table, constant int refers to constant expressions of type int(such as '0' or variables declared final).

For the final table row, S1 and S2 are the types of the second and third operands, respectively. T1 is the type that results from applying boxing conversion to S1, and T2 is the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion to the least upper bound of T1 and T2. See §5.1.7, “Boxing Conversion,” §5.1.10, “Capture Conversion,” and §15.12.2.7, “Inferring Type Arguments Based on Actual Arguments” of the JLS for additional information [JLS 2013].

The complexity of the rules that determine the result type of a conditional expression can result in unintended type conversions. Consequently, the second and third operands of each conditional expression should have identical types. This recommendation also applies to boxed primitives.

Noncompliant Code Example

In this noncompliant code example, the programmer expects that both print statements will print the value of alpha as a char:


public class Expr {
public static void main(String[] args) {
char alpha = 'A';
int i = 0;
// Other code. Value of i may change
boolean trueExp = true; // Expression that evaluates to true
System.out.print(trueExp ? alpha : 0); // Prints A
System.out.print(trueExp ? alpha : i); // Prints 65
}
}


The first print statement prints A because the compiler applies rule 8 from the result type determination table to determine that the second and third operands of the conditional expression are, or are converted to, type char. However, the second print statement prints 65—the value of alphaas an int. The first matching rule from the table is rule 10. Consequently, the compiler promotes the value of alpha to type int.

Compliant Solution

This compliant solution uses identical types for the second and third operands of each conditional expression; the explicit casts specify the type expected by the programmer:


public class Expr {
public static void main(String[] args) {
char alpha = 'A';
int i = 0;
boolean trueExp = true; // Expression that evaluates to true
System.out.print(trueExp ? alpha : 0); // Prints A
// Deliberate narrowing cast of i; possible truncation OK
System.out.print(trueExp ? alpha : ((char) i)); // Prints A
}
}


When the value of i in the second conditional expression falls outside the range that can be represented as a char, the explicit cast will truncate its value. This usage complies with exception NUM12-EX0 of NUM12-J, “Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data” in The CERT® Oracle® Secure Coding Standard for Java [Long 2012].

Noncompliant Code Example

This noncompliant code example prints 100 as the size of the HashSet rather than the expected result (some value between 0 and 50):


public class ShortSet {
public static void main(String[] args) {
HashSet<Short> s = new HashSet<Short>();
for (short i = 0; i < 100; i++) {
s.add(i);
// Cast of i-1 is safe,
// because value is always representable
Short workingVal = (short) (i-1);
// ... Other code may update workingVal

s.remove(((i % 2) == 1) ? i-1 : workingVal);
}
System.out.println(s.size());
}
}


The combination of values of types short and int in the second argument of the conditional expression (the operation i-1) causes the result to be an int, as specified by the integer promotion rules. Consequently, the Short object in the third argument is unboxed into a short, which is then promoted to an int. The result of the conditional expression is then autoboxed into an object of type Integer. Because the HashSet contains only values of type Short, the call to HashSet.remove() has no effect.

Compliant Solution

This compliant solution casts the second operand to type short, then explicitly invokes the Short.valueOf() method to create a Short instance whose value is i-1:


public class ShortSet {
public static void main(String[] args) {
HashSet<Short> s = new HashSet<Short>();
for (short i = 0; i < 100; i++) {
s.add(i);
// Cast of i-1 is safe, because the
// resulting value is always representable
Short workingVal = (short) (i-1);
// ... other code may update workingVal

// Cast of i-1 is safe, because the
// resulting value is always representable
s.remove(((i % 2) == 1) ? Short.valueOf((short) (i-1)) :
workingVal);
}
System.out.println(s.size());
}
}


As a result of the cast, the second and third operands of the conditional expression both have type Short, and the remove() call has the expected result.

Writing the conditional expression as ((i % 2) == 1) ? (short) (i-1)) : workingVal also complies with this guideline because both the second and third operands in this form have type short. However, this alternative is less efficient because it forces unboxing of workingVal on each even iteration of the loop and autoboxing of the result of the conditional expression (from short to Short) on every iteration of the loop.

Applicability

When the second and third operands of a conditional expression have different types, they can be subject to unexpected type conversions.

Automated detection of condition expressions whose second and third operands are of different types is straightforward.

Bibliography

[Bloch 2005]

Puzzle 8, “Dos Equis”

[Findbugs 2008]

“Bx: Primitive Value Is Unboxed and Coerced for Ternary Operator”

[JLS 2013]

§15.25, “Conditional Operator ? :”

[Long 2012]

NUM12-J. Ensure conversions of numeric types to narrower types do not result in lost or misinterpreted data

46. Do not serialize direct handles to system resources

Serialized objects can be altered outside of any Java program unless they are protected using mechanisms such as sealing and signing. (See The CERT® Oracle® Secure Coding Standard for Java [Long 2012], “ENV01-J. Place all security-sensitive code in a single JAR and sign and seal it.”) If an object referring to a system resource becomes serialized, and an attacker can alter the serialized form of the object, it becomes possible to modify the system resource that the serialized handle refers to. For example, an attacker may modify a serialized file handle to refer to an arbitrary file on the system. In the absence of a security manager, any operations that use the file handle will be carried out using the attacker-supplied file path and file name.

Noncompliant Code Example

This noncompliant code example declares a serializable File object in the class Ser:


final class Ser implements Serializable {
File f;
public Ser() throws FileNotFoundException {
f = new File("c:\\filepath\\filename");
}
}


The serialized form of the object exposes the file path, which can be altered. When the object is deserialized, the operations are performed using the altered path, which can cause the wrong file to be read or modified.

Compliant Solution (Not Implementing Serializable)

This compliant solution shows a final class Ser that does not implement java.io.Serializable. Consequently, the File object cannot be serialized.


final class Ser {
File f;
public Ser() throws FileNotFoundException {
f = new File("c:\\filepath\\filename");
}
}


Compliant Solution (Object Marked Transient)

This compliant solution declares the File object transient. The file path is not serialized with the rest of the class, and is consequently not exposed to attackers.


final class Ser implements Serializable {
transient File f;
public Ser() throws FileNotFoundException {
f = new File("c:\\filepath\\filename");
}
}


Applicability

Deserializing direct handles to system resources can allow the modification of the resources being referred to.

Bibliography

[Long 2012]

ENV01-J. Place all security-sensitive code in a single JAR and sign and seal it

[Oracle 2013c]

Java Platform Standard Edition 7 Documentation

47. Prefer using iterators over enumerations

According to the Java API Interface Enumeration<E> documentation [API 2013],

An object that implements the Enumeration interface generates a series of elements, one at a time. Successive calls to the nextElement method return successive elements of the series.

As an example, the following code uses an Enumeration to display the contents of a Vector:


for (Enumeration e = vector.elements(); e.hasMoreElements();) {
System.out.println(e.nextElement());
}


The Java API [API 2013] recommends, “New implementations should consider using Iterator in preference to Enumeration.” Iterators are superior to enumerations because they use simpler method names, and, unlike enumerations, iterators have well-defined semantics when elements in a collection are removed while iterating over the collection. Consequently, iterators rather than enumerators should be preferred when examining iterable collections.

Noncompliant Code Example

This noncompliant code example implements a BankOperations class with a removeAccounts() method used to terminate all the accounts of a particular account holder, as identified by the name. Names can be repeated in the vector if a person has more than one account. The remove() method attempts to iterate through all the vector entries, comparing each entry with the name “Harry.”


class BankOperations {
private static void removeAccounts(Vector v, String name) {
Enumeration e = v.elements();

while (e.hasMoreElements()) {
String s = (String) e.nextElement();
if (s.equals(name)) {
v.remove(name); // Second Harry is not removed
}
}

// Display current account holders
System.out.println("The names are:");
e = v.elements();
while (e.hasMoreElements()) {
// Prints Dick, Harry, Tom
System.out.println(e.nextElement());
}
}

public static void main(String args[]) {
// List contains a sorted array of account holder names
// Repeats are admissible
List list = new ArrayList(Arrays.asList(
new String[] {"Dick", "Harry", "Harry", "Tom"}));
Vector v = new Vector(list);
removeAccount(v, "Harry");
}
}


Upon encountering the first “Harry,” it successfully removes the entry, and the size of the vector diminishes to three. However, the index of the Enumeration remains unchanged, causing the program to perform the next (now final) comparison with “Tom.” Consequently, the second “Harry” remains in the vector unscathed, having shifted to the second position in the vector.

Compliant Solution

According to the Java API Interface Iterator<E> documentation [API 2013],

Iterator takes the place of Enumeration in the Java collections framework. Iterators differ from enumerations in two ways:

Image Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.

Image Method names have been improved.

This compliant solution remedies the problem described in the noncompliant code example and demonstrates the advantages of using an Iterator over an Enumeration:


class BankOperations {
private static void removeAccounts(Vector v, String name) {
Iterator i = v.iterator();

while (i.hasNext()) {
String s = (String) i.next();
if (s.equals(name)) {
i.remove(); // Correctly removes all instances
// of the name Harry
}
}

// Display current account holders
System.out.println("The names are:");
i = v.iterator();
while (i.hasNext()) {
System.out.println(i.next()); // Prints Dick, Tom only
}
}

public static void main(String args[]) {
List list = new ArrayList(Arrays.asList(
new String[] {"Dick", "Harry", "Harry", "Tom"}));
Vector v = new Vector(list);
remove(v, "Harry");
}
}


Applicability

Using Enumeration when performing remove operations on an iterable Collection may cause unexpected program behavior.

Bibliography

[API 2013]

Interface Enumeration<E>

Interface Iterator<E>

[Daconta 2003]

Item 21, “Use Iteration over Enumeration”

48. Do not use direct buffers for short-lived, infrequently used objects

The new I/O (NIO) classes in java.nio allow the creation and use of direct buffers. These buffers tremendously increase throughput for repeated I/O activities. However, their creation and reclamation is more expensive than the creation and reclamation of heap-based non-direct buffers because direct buffers are managed using OS-specific native code. This added management cost makes direct buffers a poor choice for single-use or infrequently used cases. Direct buffers are also outside the scope of Java’s garbage collector; consequently, injudicious use of direct buffers can cause memory leaks. Finally, frequent allocation of large direct buffers can cause an OutOfMemoryError.

Noncompliant Code Example

This noncompliant code example uses both a short-lived local object, rarelyUsed-Buffer, and a long-lived, heavily used object, heavilyUsedBuffer. Both are allocated in non-heap memory; neither is garbage collected.


ByteBuffer rarelyUsedBuffer = ByteBuffer.allocateDirect(8192);
// Use rarelyUsedBuffer once

ByteBuffer heavilyUsedBuffer = ByteBuffer.allocateDirect(8192);
// Use heavilyUsedBuffer many times


Compliant Solution

This compliant solution uses an indirect buffer to allocate the short-lived, infrequently used object. The heavily used buffer appropriately continues to use a non-heap, non-garbage-collected direct buffer.


ByteBuffer rarelyUsedBuffer = ByteBuffer.allocate(8192);
// Use rarelyUsedBuffer once

ByteBuffer heavilyUsedBuffer = ByteBuffer.allocateDirect(8192);
// Use heavilyUsedBuffer many times


Applicability

Direct buffers are beyond the scope of Java’s garbage collector and can cause memory leaks if they are used injudiciously. In general, direct buffers should be allocated only when their use provides a significant gain in performance.

Bibliography

[API 2013]

Class ByteBuffer

49. Remove short-lived objects from long-lived container objects

Always remove short-lived objects from long-lived container objects when the task is over. For example, objects attached to a java.nio.channels.SelectionKey object must be removed when they are no longer needed. Doing so reduces the likelihood of memory leaks. Similarly, use of array-based data structures such as ArrayList can introduce a requirement to indicate the absence of an entry by explicitly setting ArrayList’s individual array element to null.

This guideline specifically addresses objects referred to from containers. For an example where nulling out objects does not aid garbage collection, see Guideline 75, “Do not attempt to help the garbage collector by setting local reference variables to null.”

Noncompliant Code Example (Removing Short-Lived Objects)

In this noncompliant code example, a long-lived ArrayList contains references to both long- and short-lived elements. The programmer marks elements that have become irrelevant by setting a “dead” flag in the object.


class DataElement {
private boolean dead = false;
// Other fields

public boolean isDead() { return dead; }
public void killMe() { dead = true; }
}

// ... Elsewhere

List<DataElement> longLivedList = new ArrayList<DataElement>();

// Processing that renders an element irrelevant

// Kill the element that is now irrelevant
longLivedList.get(someIndex).killMe();


The garbage collector cannot collect the dead DataElement object until it becomes unreferenced. Note that all methods that operate on objects of class DataElement must also check whether the instance in hand is dead.

Compliant Solution (Set Reference to null)

In this compliant solution, rather than use a dead flag, the programmer assigns null to ArrayList elements that have become irrelevant:


class DataElement {
// Dead flag removed
// Other fields
}

// Elsewhere
List<DataElement> longLivedList = new ArrayList<DataElement>();

// Processing that renders an element irrelevant

// Set the reference to the irrelevant DataElement to null
longLivedList.set(someIndex, null);


Note that all code that operates on the longLivedList must now check for list entries that are null.

Compliant Solution (Use Null Object Pattern)

This compliant solution avoids the problems associated with intentionally null references by using a singleton sentinel object. This technique is known as the Null Object pattern (also known as the Sentinel pattern).


class DataElement {
public static final DataElement NULL = createSentinel();
// Dead flag removed
// Other fields

private static final DataElement createSentinel() {
// Allocate a sentinel object, setting all its fields
// to carefully chosen "do nothing" values
}
}

// Elsewhere
List<DataElement> longLivedList = new ArrayList<DataElement>();

// Processing that renders an element irrelevant
// Set the reference to the irrelevant DataElement to
// the NULL object
longLivedList.set(someIndex, DataElement.NULL);


When feasible, programmers should choose this design pattern over the explicit null reference values, as described in Guideline 41, “Return an empty array or collection instead of a null value for methods that return an array or collection.”

When using this pattern, the null object must be a singleton and must be final. It may be either public or private, depending on the overall design of the DataElement class. The state of the null object should be immutable after creation; immutability can be enforced either by using finalfields or by explicit code in the methods of the DataElement class. See Chapter 8, “Behavioral Patterns, the Null Object,” of Patterns in Java, Volume 1, Second Edition [Grand 2002], for additional information on this design pattern, and also The CERT® Oracle® Secure Coding Standard for Java [Long 2012], “ERR08-J. Do not catch NullPointerException or any of its ancestors.”

Applicability

Leaving short-lived objects in long-lived container objects may consume memory that cannot be recovered by the garbage collector, leading to memory exhaustion and possible denial of service attacks.

Bibliography

[Grand 2002]

Chapter 8, “Behavioral Patterns, the Null Object”

[Long 2012]

ERR08-J. Do not catch NullPointerException or any of its ancestors