Serialization can extend the lifetime of objects, consequently preventing their garbage collection of those objects. The ObjectOutputStream
ensures that each object is written to the stream only once by retaining a reference (or handle) to each object written to the stream. When an a previously - written object is subsequently written to the stream again, it is replaced with a reference to the originally - written data in the stream. Note that this substitution takes place without regard to regardless whether the object's contents have changed in the interim. It requires a table of references to be maintained to keep track of previously serialized objects. This table of references prevents garbage collection of the previously -written serialized objects because the garbage collector cannot collect object instances referred to by live references.
This behavior is both desirable and correct for data that potentially contains may contain arbitrary object graphs, especially when the graphs are fully allocated and constructed prior to serialization. However, it can lead to memory exhaustion when serializing data that both lacks references to other objects being serialized and also can be allocated in part or in full after serialization has begun. One such example of such data is serializing a data stream from an external sensor. In such cases, programs must take additional action to avoid memory exhaustion. That is, programs reading in independent serialized data must reset the object cache table of references between reads to prevent memory exhaustion.
This rule is a specific instance of the more general rule MSC07MSC05-J. Do not assume infinite exhaust heap space.
Noncompliant Code Example
This noncompliant code example reads and serializes data from an external sensor. Each invocation of the readSensorData()
method returns a newly created SensorData
instance, each containing a one megabyte of data. SensorData
instances contain are pure data streams, containing data and arrays , but lack any lacking references to other SensorData
objects; this is a pure data stream.
The As already described, the ObjectOutputStream
maintains a cache of previously - written objects, as discussed above; consequently, . Consequently, all SensorData
objects remain alive until the cache itself becomes garbage-collected. This can result in an OutOfMemoryError
, An OutOfMemoryError
can occure because the stream remains open while new objects are being written to it.
Code Block | ||
---|---|---|
| ||
class SensorData implements Serializable { // 1 1MBMB of data per instance! ... public static SensorData readSensorData() {...} public static boolean continueReadingisAvailable() {...} } class SerializeSensorData { public static void main(String[] args) throws IOException { ObjectOutputStream out = null; try { out = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream("ser.dat"))); while (SensorData.continueReadingisAvailable()) { // noteNote that each SensorData object is 1 1MBMB in size SensorData sd = SensorData.readSensorData(); out.writeObject(sd); } } finally { if (out != null) { out.close(); } } } } |
Compliant Solution
This compliant solution takes advantage of the known properties of the sensor data by resetting the output stream after each write. The reset clears the output stream's internal object cache; consequently, the cache no longer maintains references to previously - written SensorData
objects. The garbage collector is able to can collect SensorData
instances that are no longer needed.
Code Block | ||
---|---|---|
| ||
class SerializeSensorData { public static void main(String[] args) throws IOException { ObjectOutputStream out = null; try { out = new ObjectOutputStream( new BufferedOutputStream(new FileOutputStream("ser.dat"))); while (SensorData.continueReadingisAvailable()) { // noteNote that each SensorData object is 1 1MBMB in size SensorData sd = SensorData.readSensorData(); out.writeObject(sd); out.reset(); // resetReset the stream } } finally { if (out != null) { out.close(); } } } } |
Risk Assessment
Memory and resource leaks during serialization can consume all available memory or crash the JVMresult in a resource exhaustion attack or can crash the Java Virtual Machine.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
SER10-J |
Low |
Unlikely |
Low | P3 | L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c0cf2335-d86e-4e82-ac67-6339facaf134"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] |
| ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="d8fd659c-9e91-4afe-8944-fd8db25c2061"><ac:plain-text-body><![CDATA[ | [[Harold 2006 | AA. Bibliography#Harold 06]] | 13.4. Performance | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c7517c86-9e56-42ba-8212-38949bce0236"><ac:plain-text-body><![CDATA[ | [[Sun 2006 | AA. Bibliography#Sun 06]] | "Serialization specification" | ]]></ac:plain-text-body></ac:structured-macro> |
Automated Detection
Detecting code that should be considered privileged or sensitive requires programmer assistance. Given identified privileged code as a starting point, automated tools could compute the closure of all code that can be invoked from that point. Such a tool could plausibly determine whether all code in that closure exists within a single package. A further check of whether the package is sealed is feasible.
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
CodeSonar |
| JAVA.ALLOC.LEAK.NOTSTORED | Closeable Not Stored (Java) |
Related Guidelines
CWE-400, Uncontrolled Resource Consumption (aka "Resource Exhaustion") |
Bibliography
...
SER09-J. Do not invoke overridable methods from the readObject method 16. Serialization (SER)