Wiki Markup |
---|
Even though Java supports memory management through garbage collection, there are innumerable possibilities of introducing memory leaks as a result of programming errors. Furthermore, the garbage collector collects only the unreachable objects and not those that are still reachable. The presence of reachable objects that remain unused indicates memory mismanagement. |
...
Depending on program scale and available memory, one of the least desirable errors, the {{OutOfMemoryError}} manifests itself when the heap space runs out. This usually results in program failure. |
...
h2. Noncompliant Code Example |
...
This noncompliant code example shows a leaking {{Vector}} object. The memory leak quickly exhausts the heap space as the condition for removing the {{vector}} element is mistakenly written as {{n > 0}} instead of {{n >= 0}}. As a result, in every iteration, the method leaks one {{vector}} element. |
...
Code Block | ||
---|---|---|
| ||
{code:bgColor=#FFCCCC} public class Leak { static Vector vector = new Vector(); public void leakingVector(int count) { for (int n = 0; n < count; n++) { vector.add(Integer.toString(n)); } for (int n = count - 1; n > 0; n--) { // Free the memory vector.removeElementAt(n); } } public static void main(String[] args) throws IOException { Leak le = new Leak(); int i = 1; while(true) { System.out.println("Iteration: " + i); le.leakingVector(1); i++; } } } {code} h2. Compliant Solution (1) |
...
This compliant solution corrects the mistake by changing the loop condition to {{n >= 0 |
...
Code Block | ||
---|---|---|
| ||
}}.
{code:bgColor=#ccccff}
for (int n = count - 1; n >= 0; n--) {
vector.removeElementAt(n);
}
|
...
{code} h2. Compliant Solution (2) |
...
Prefer the use of standard language semantics where possible, as shown in this compliant solution. |
...
{code | ||
:bgColor | =#ccccff | }
while (!vector.isEmpty()){
vector.removeElementAt(vector.size() - 1);
}
|
...
{code} h2. Compliant Solution (3) |
...
An alternative way of clearing the {{vector}} is to use the {{vector.clear()}} method. Likewise, if a range of elements have to be removed from the {{vector}}, {{vector.subList(fromIndex, toIndex).clear()}} can be used. In the context of this compliant solution, the {{fromIndex}} and the {{toIndex}} can both be {{0}} as the {{count}} variable is {{1}} on each iteration. \[[API 06|AA. Java References#API 06]\] |
...
{code | ||
:bgColor | =#ccccff | } vector.clear(); |
Noncompliant Code Example
This noncompliant code example creates a HashMap
instance field within the class body but uses it only in the doSomething()
method. It is not obvious that it will continue to persist as long as the class BadScope
's instance is alive.
Code Block | ||
---|---|---|
| ||
{code}
h2. Noncompliant Code Example
This noncompliant code example creates a {{HashMap}} instance field within the class body but uses it only in the {{doSomething()}} method. It is not obvious that it will continue to persist as long as the class {{BadScope}}'s instance is alive.
{code:bgColor=#FFCCCC}
public class BadScope {
private HashMap<Integer,String> hm = new HashMap<Integer, String>();
private void doSomething() {
hm.put(1, "java"); // hm is used only here
}
}
|
Compliant Solution
Localizing or confining the instance field to a narrower scope gives the garbage collector a better chance of succeeding at collecting the object in a timely manner. Short-lived objects are always collected quickly by generational garbage collectors.
Code Block | ||
---|---|---|
| ||
{code} h2. Compliant Solution Localizing or confining the instance field to a narrower scope gives the garbage collector a better chance of succeeding at collecting the object in a timely manner. Short-lived objects are always collected quickly by generational garbage collectors. {code:bgColor=#ccccff} public class GoodScope { private void doSomething() { HashMap<Integer,String> hm = new HashMap<Integer,String>(); hm.put(1,"java"); } } {code} h2. Noncompliant Code Example |
...
This noncompliant code example demonstrates unintentional object retention and is commonly called the _Lapsed Listener_. The {{button}} continues to hold a reference of the {{reader}} object even after completion of the {{readSomething()}} method. As a result, the garbage collector does not collect the {{reader}} object. A similar problem occurs with inner classes as they hold an implicit reference to the outer class. |
...
{code | ||
:bgColor | =#FFCCCC | }
public class LapseEvent extends JApplet {
JButton button;
public void init() {
button = new JButton("Click Me");
getContentPane().add(button, BorderLayout.CENTER);
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething();
} catch (IOException e) {
// Handle exception
}
}
}
class Reader implements ActionListener{
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().beep();
}
public void readSomething() throws IOException {
// Read from file
}
}
|
Noncompliant Code Example
To resolve this problem, a matching pair of the removeActionListener()
should be used, as shown below. Unfortunately, this is not the panacea because an exception in the reader.readSomething()
method can change the control flow in such a way that the removeActionListener
statement is never executed.
Code Block | ||
---|---|---|
| ||
{code}
h2. Noncompliant Code Example
{mc} Bloch 08 says: The best way to ensure that callbacks are garbage collected promptly is to store only weak references to them, for instance, by storing them only as keys in a WeakHashMap. {mc}
To resolve this problem, a matching pair of the {{removeActionListener()}} should be used, as shown below. Unfortunately, this is not the panacea because an exception in the {{reader.readSomething()}} method can change the control flow in such a way that the {{removeActionListener}} statement is never executed.
{code:bgColor=#FFCCCC}
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething(); // Can skip next line
button.removeActionListener(reader); // Dereferenced, but control flow can change
} catch (IOException e) {
// Forward to handler
}
|
Compliant Solution
This compliant solution uses the finally
block to ensure that the reader
object's reference is unregistered.
Code Block | ||
---|---|---|
| ||
{code} h2. Compliant Solution This compliant solution uses the {{finally}} block to ensure that the {{reader}} object's reference is unregistered. {code:bgColor=#ccccff} Reader reader = new Reader(); button.addActionListener(reader); try { reader.readSomething(); } catch (IOException e) { // Handle exception } finally { button.removeActionListener(reader); // Always executed } {code} h2. Noncompliant Code |
...
Example This noncompliant code example implements a {{stack}} data structure \[[Bloch 08|AA. Java References#Bloch 08]\]. The main issue is that it does not allow the garbage collector to deallocate memory after the {{pop}} operation. The object references are retained even after the element is pop'ed. Such _obsolete references_ are not garbage collected automatically. This can get even more deceitful because none of the objects referenced by the offending object get garbage collected. |
...
{code | ||
:bgColor | =#FFCCCC | } public class Stack { private Object[] elements; private int size = 0; public Stack(int initialCapacity) { this.elements = new Object[initialCapacity]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { // This method causes memory leaks if (size == 0) { throw new EmptyStackException(); } return elements[--size]; } /** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) { Object[] oldElements = elements; elements = new Object[2 * elements.length + 1]; System.arraycopy(oldElements, 0, elements, 0, size); } } } {code} h2. Compliant Solution |
...
This compliant solution assigns {{null}} values to all obsolete references. The garbage collector can now include individual element-objects in its list of objects to free. A {{NullPointerException}} results on subsequent attempts to access the particular object. |
...
{code | ||
:bgColor | =#ccccff | } public Object pop() { if (size==0) throw new EmptyStackException(); // Ensures object consistency Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } |
...
{code} While these examples may not model production scenarios, it is not uncommon to uncover _obsolete references_ when dealing with data structures such as hash tables that contain many large-sized records. It is prudent to assign {{null}} to array-like custom data structures, however, doing so with individual objects or local variables has no specific advantages. The garbage collector is sufficiently equipped to handle these cases. \[[Commes 07|AA. Java References#Commes 07]\] |
...
h2. Noncompliant Code Example |
...
A common variant of the aforementioned noncompliant code example is the unintentional retention of objects when using a {{Map}} or a similar {{Collections}} object. In this noncompliant code example, a server maintains temporary metadata about all secure connections it commits to. Although the metadata is designed to be {{transient}} by nature, it persists even after the particular socket is closed. Unless some notification logic is installed, it is impossible to determine the best time to eliminate the object reference. Likewise, nulling out original objects or referents (Socket connections) by itself, proves to be unwieldy. |
...
Code Block | ||
---|---|---|
| ||
{code:bgColor=#FFCCCC}
class HashMemLeak {
private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(new HashMap<SSLSocket, InetAddress>());
public void storeTempConnection(SSLSocket sock, InetAddress ip) {
m.put(sock, ip);
}
public void removeTempConnection(SSLSocket sock) {
m.remove(sock);
}
}
|
Compliant Solution
...
{code} h2. Compliant Solution This compliant solution uses _weak references_ to ameliorate the issue. Strong references typically used in code, do not allow the garbage collector to reclaim the objects that are stored compositely, such as in a {{Map}}. According to the Java API \[[API 06|AA. Java References#API 06]\], weak reference objects: "... do not prevent their referents from being made finalizable, finalized, and then reclaimed". |
...
A referent is the object that is being referred to. As soon as any strong references to the object are found to have phased out, the garbage collector reclaims the referent. With {{WeakHashMap}}, the map's _key_ is weakly referred to and as a result determines whether the corresponding referents are ready to be collected. |
...
Code Block | ||
---|---|---|
| ||
{code:bgColor=#ccccff} // ... private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(new WeakHashMap<SSLSocket, InetAddress>()); // ... |
...
{code} It is not enough to facilitate the collection of unneeded objects with weak references. It is critical to prune the data structure so that more entries can be accommodated in the newly created space. This can be achieved by calling the {{get()}} method of {{WeakHashMap}} and removing the entry that corresponds to the {{null}} return value (polling). A more efficient method is to use a reference queue. \[[Goetz 05b|AA. Java References#Goetz 05b]\] |
...
h2. Compliant Solution |
...
If the referent is assigned the value {{null}}, it is eventually garbage collected. However, the {{HashMap}} continues to strongly reference the {{WeakReference}} object and the corresponding value (for each entry in the {{HashMap}}). As soon as the GC clears the reference (which referred to the referent), it adds the corresponding {{WeakReference}} object to the reference queue. It remains there unless some operation is performed on the queue (such as a {{put()}} or {{remove()}}). After such an operation, the {{WeakReference}} object in the hashmap is also collected. Alternatively, this two-step procedure can be carried out manually by using the following code: |
...
{code | ||
:bgColor | =#ccccff | } ReferenceQueue queue = new ReferenceQueue(); WeakReference wr = new WeakReference(key, queue); // Two-arg constructor, key = 'sock' hashmap.put(wr, value); while ((wr = (WeakReference) queue.poll()) != null) { hashmap.remove(wr); // Removes the WeakReference object and the value (not the referent) } {code} Note that the two-argument constructor of {{WeakReference}} takes a {{Queue}} argument and must be used to perform direct queue processing. |
...
Compliant Solution
It is also permissible to use soft references because they guarantee that the referent will be reclaimed before an OutOfMemoryError
results but no sooner than the time when memory begins to run out.
Risk Assessment
Memory leaks in Java applications may be exploited to cause denial of service.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MSC06- J | low | unlikely | high | P1 | L3 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
Wiki Markup |
---|
\[[API 06|AA. Java References#API 06]\] Class Vector, Class WeakReference
\[[Gupta 05|AA. Java References#Gupts 05]\]
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 6: Eliminate obsolete object references
\[[Commes 07|AA. Java References#Commes 07]\] Memory Leak Avoidance
\[[Goetz 05|AA. Java References#Goetz 05]\] Lapsed listeners
\[[Goetz 05b|AA. Java References#Goetz 05b]\] "Memory leaks with global Maps" and "Reference queues"
\[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 401|http://cwe.mitre.org/data/definitions/401.html] "Failure to Release Memory Before Removing Last Reference (aka 'Memory Leak')" |
...
h2. Compliant Solution
It is also permissible to use soft references because they guarantee that the referent will be reclaimed before an {{OutOfMemoryError}} results but no sooner than the time when memory begins to run out.
h2. Risk Assessment
Memory leaks in Java applications may be exploited to cause denial of service.
|| Rule || Severity || Likelihood || Remediation Cost || Priority || Level ||
| MSC06- J | low | unlikely | high | {color:green}{*}P1{*}{color} | {color:green}{*}L3{*}{color} |
h3. Automated Detection
TODO
h3. Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MSC01-J].
h2. References
\[[API 06|AA. Java References#API 06]\] Class Vector, Class WeakReference
\[[Gupta 05|AA. Java References#Gupts 05]\]
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 6: Eliminate obsolete object references
\[[Commes 07|AA. Java References#Commes 07]\] Memory Leak Avoidance
\[[Goetz 05|AA. Java References#Goetz 05]\] Lapsed listeners
\[[Goetz 05b|AA. Java References#Goetz 05b]\] "Memory leaks with global Maps" and "Reference queues"
\[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 401|http://cwe.mitre.org/data/definitions/401.html] "Failure to Release Memory Before Removing Last Reference (aka 'Memory Leak')"
----
[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_left.png!|MSC05-J. Make sensitive classes noncloneable] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_up.png!|49. Miscellaneous (MSC)] [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_right.png!|MSC07-J. Eliminate class initialization cycles]
|