From a security point of view, Java's s garbage-collection feature provides significant benefits over traditional languages such as C and C++non-garbage-collected languages. The garbage collector (GC) is designed to automatically reclaim unreachable memory and as a result to avoid memory leaks. While it Although the GC is quite adept at performing this task, a malicious attacker can nevertheless launch a Denial denial-of Service -service (DoS) attack against the GC, such as by inducing abnormal heap memory allocation as well as or abnormally prolonged object retention. For example, some versions of the GC needs could need to halt all executing threads to keep up with the incoming allocation requests that command trigger increased heap management in terms of space allocationactivity. System throughput rapidly diminishes in this scenario.
Real-time systems, in particular, are vulnerable to a more subtle slow-heap-exhaustion DoS attack, perpetrated by stealing CPU cycles. An attacker can source perform memory allocations in a way that keeps resource increases the consumption of resources (such as CPU, battery power, and memory) high without triggering an OutOfMemoryError
. Writing garbage collection friendly -collection–friendly code helps restrict many attack avenues. The best practices have been collated and enumerated below.
Use Short-Lived Immutable Objects
Since JDK Beginning with JDK 1.2, the new generational garbage collector has reduced memory allocation related costs to minimal levels, even lesser than C/C++. Deallocation has also become cheaper such that the cost of garbage collection is commensurate with the number of _live_ objects in the _younger generation_ and not the _total_ number of objects allocated since the last run. Memory is managed in generations to optimize the collection. The younger generation consists of short-lived objects. A minor collection on the younger generation is performed when it fills up with dead objects \[[Oracle 2010a|AA. Java References#Oracle 10a]\]. Wiki Markup
Wiki Markup |
---|
Note that objects in the _younger generation_ that persist for longer durations are _tenured_ and are moved to the _tenured generation_. Very few _younger generation_ objects continue to live through to the next garbage collection cycle; the rest become ready to be collected in the impending collection cycle \[[Oracle 2010a|AA. Java References#Oracle 10a]\]. |
With generational GCs it is advantageous to use short-lived immutable objects instead of long-lived mutable objects. Object pools are examples of the latter and should be avoided to increase the garbage collector's efficiency. Moreover, object pools can create synchronization problems, deallocations have to be managed explicitly leading to dangers of dangling pointers, and the size of the pool plays a dominant role in mission critical code. Exceptions to this recommendation can be made when the allocation takes longer in comparison, such as when performing multiple joins across databases or when using objects that represent scarce resources such as thread pools and database connections.
Noncompliant Code Example
Wiki Markup |
---|
This noncompliant code example (based on \[[Goetz 2004|AA. Java References#Goetz 04]\]) shows a container, {{MutableHolder}}. In {{MutableHolder}}, the instance field {{value}} can be updated to reference a new value using the {{setValue()}} method which makes its existence long-term. This slows down garbage collection. |
Code Block | ||
---|---|---|
| ||
public class MutableHolder {
private Hashtable<Integer, String> value; // not final
public Object getValue() {
return value;
}
public void setValue(Hashtable<Integer, String> ht) {
value = (Hashtable<Integer, String>)ht;
}
}
|
This example also violates guideline FIO00-J. Defensively copy mutable inputs and mutable internal components.
Compliant Solution
This compliant solution highlights a custom container called ImmutableHolder
. To aid garbage collection, it is recommended that short-lived ImmutableHolder
objects be created by passing Hashtable
instances to the constructor.
Code Block | ||
---|---|---|
| ||
public class ImmutableHolder {
private final Hashtable<Integer, String> value;
// create defensive copy of inputs
public ImmutableHolder(Hashtable<Integer, String> ht) {
value = (Hashtable<Integer, String>)ht.clone();
}
// create defensive copy while returning
public Object getValue() {
return value.clone();
}
}
|
When value
is assigned in ImmutableHolder
's constructor during object creation, it is a younger member field (of type ImmutableHolder.Hashtable<Integer, String>
) that is referencing an older object in the client code (of the same type Hashtable<Integer, String>
). This is a much better position to be in as far as the garbage collector is concerned. Note that a shallow copy is used in this case to preserve references to the older value.
Avoid Large Objects
generational GC has reduced memory allocation costs, in many cases to levels lower than in C or C++. Generational garbage collection reduces garbage-collection costs by grouping objects into generations. The younger generation consists of short-lived objects. The GC performs a minor collection on the younger generation when it fills up with dead objects [Oracle 2010a]. Improved garbage-collection algorithms have reduced the cost of garbage collection so that it is proportional to the number of live objects in the younger generation rather than to the number of objects allocated since the last garbage collection.
Note that objects in the younger generation that persist for longer durations are tenured and are moved to the tenured generation. Few younger-generation objects continue to live through to the next garbage-collection cycle. The rest become ready to be collected in the impending collection cycle [Oracle 2010a].
With generational GCs, use of short-lived immutable objects is generally more efficient than use of long-lived mutable objects, such as object pools. Avoiding object pools improves the GC's efficiency. Object pools bring additional costs and risks: they can create synchronization problems and can require explicit management of deallocations, possibly creating problems with dangling pointers. Further, determining the correct amount of memory to reserve for an object pool can be difficult, especially for mission-critical code. Use of long-lived mutable objects remains appropriate when allocation of objects is particularly expensive (for example, when performing multiple joins across databases). Similarly, object pools are an appropriate design choice when the objects represent scarce resources, such as thread pools and database connections.
Avoid Large Objects
The allocation of large objects is expensive, in part because the cost to initialize their fields is proportional to their size. Additionally, frequent allocation of The allocation for large objects is expensive and the initialization cost is proportional to their size. Sometimes large objects of different sizes can cause fragmentation issues or non compacting collect.
Do Not Use Direct Buffers for Short Lived, Infrequently Used Objects
The new IO classes (NIO) in java.nio
allow the creation and use of direct buffers. These buffers tremendously increase throughput for repeated IO activities, however, their creation and reclamation for one-time use is more expensive than heap based non-direct buffers. This is because OS specific native code is used to manage them. An OutOfMemoryError
may result if large objects are allocated frequently using this technique. Direct buffers are also not subject to Java's garbage collector which may cause memory leaks.
Noncompliant Code Example
This noncompliant code example uses a short-lived local object buffer
. The buffer
is allocated in non-heap memory and is not garbage collected.
Code Block | ||
---|---|---|
| ||
ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
// use buffer once
|
Compliant Solution
This compliant solution uses an indirect buffer to allocate the short-lived, infrequently used object.
Code Block | ||
---|---|---|
| ||
ByteBuffer buffer = ByteBuffer.allocate(8192);
// use buffer once
|
Nulling References
Reference nulling to "help the garbage collector" is unnecessary. In fact, it just adds clutter to the code and sometimes introduces subtle bugs. Assigning null
to local variables is also not very useful as the Java Just-In-Time compiler (JIT) can equivalently do a liveness analysis. A related bad practice is to use a finalizer to null
out references. This practice can cause a huge performance hit.
Noncompliant Code Example
This noncompliant code example assigns null
to the buffer
array.
Code Block | ||
---|---|---|
| ||
int[] buffer = new int[100];
doSomething(buffer);
buffer = null // No need to explicitly assign null
|
Compliant Solution
Wiki Markup |
---|
This compliant solution improves by narrowing down the scope of the variable {{buffer}} so that the garbage collector collects the object as soon as it goes out of scope \[[Bloch 2008|AA. Java References#Bloch 08]\]. |
Code Block | ||
---|---|---|
| ||
{ // limit the scope of buffer
int[] buffer = new int[100];
doSomething(buffer);
}
|
Array based data structures such as ArrayLists
are exceptions because the programmer has to explicitly set a few of the array elements to null
to indicate their absence or demise.
Long-Lived Objects Containing Short-Lived Objects
Always remove short-lived objects from the 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 possibility of memory leaks.
compacting collect operations.
Do Not Explicitly Invoke the Garbage Collector
The garbage collector GC can be explicitly invoked by calling the System.gc()
method. Even though the documentation says that it "Runs runs the garbage collector,", there is no guarantee on as to when or whether the garbage collector GC will actually run because . In fact, the call only merely suggests that it will the GC should subsequently execute. Other reasons include the following:; the JVM is free to ignore this suggestion.
Irresponsible use of this feature can severely degrade system performance
...
by triggering garbage collection at inopportune moments rather than waiting until ripe periods when it is safe to garbage-collect without
...
significant interruption of the program'
...
s execution.
In the Java Hotspot VM (default since JDK 1.2), System.gc()
does forces an explicit garbage collection. Sometimes these Such calls are can be buried deep within libraries and are hard , so they may be difficult to trace. To ignore the call in such cases, use the flag -XX:+DisableExplicitGC
. To avoid long pauses while doing performing a full GCgarbage collection, a less demanding concurrent cycle can may be invoked by specifying the flag -XX:ExplicitGCInvokedConcurrent
.
Applicability
Misusing garbage-collection utilities can cause severe performance degradation resulting in a DoS attack.
When an There are some exceptions to this recommendation. The garbage collector can be explicitly called when the application goes through several phases like the , such as an initialization and the a ready phase. The heap needs to be compacted between these phases. Given an uneventful period, , it could require heap compaction between phases. The System.gc()
method may be explicitly invoked in this case. Also, it may be invoked as a last resort in a catch
block to recover from an OutOfMemoryError
.
Risk Assessment
Misusing some garbage collection utilities can cause Denial Of Service (DoS) related issues and severe performance degradation.
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
OBJ13-J | low | likely | high | P3 | L3 |
Automated Detection
TODO
Related Vulnerabilities
References
Wiki Markup |
---|
\[[API 2006|AA. Java References#API 06]\] Class {{System}}
\[[Commes 2007|AA. Java References#Commes 07]\] Garbage Collection Concepts and Programming Tips
\[[Goetz 2004|AA. Java References#Goetz 04]\]
\[[Lo 2005|AA. Java References#Lo 05]\]
\[[Bloch 2008|AA. Java References#Bloch 08]\] Item 6: "Eliminate obsolete object references"
\[[MITRE 2009|AA. Java References#MITRE 09]\] [CWE ID 405|http://cwe.mitre.org/data/definitions/405.html] "Asymmetric Resource Consumption (Amplification)" |
such cases, provided a suitable uneventful period occurs between phases.
Related Vulnerabilities
The Apache Geronimo and Tomcat vulnerability GERONIMO-4574, reported in March 2009, resulted from PolicyContext
handler data objects being set in a thread and never released, causing these data objects to remain in memory longer than necessary.
Bibliography
[API 2013] | |
Item 6, "Eliminate Obsolete Object References" | |
"Garbage Collection Concepts and Programming Tips" | |
Java Theory and Practice: Garbage Collection and Performance | |
[Lo 2005] | |
[Oracle 2010a] | Java SE 6 HotSpot™ Virtual Machine Garbage Collection Tuning |
...
OBJ12-J. Use checked collections against external code 08. Object Orientation (OBJ) OBJ14-J. Encapsulate the absence of an object by using a Null Object