Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

When objects are being serialized using the writeObject() method, if the same object is encountered more than once, it each object is written to the output stream only once, and after the first occurrence, only a reference to the first occurrence is written to . Invoking the writeObject() method on the same object a second time places a back-reference to the previously serialized instance in the stream. Correspondingly, the readObject() method resolves references produces at most one instance for each object present in the input stream that was previously written by writeObject() to multiple occurrences of the same object.

Wiki MarkupAccording to the Java API \[ [API 2006|AA. References#API 06]\], the {{2013], the writeUnshared()}} method:

writes an "unshared" object to the ObjectOutputStream. This method is identical to writeObject, except that it always writes the given object as a new, unique object in the stream (as opposed to a back-reference pointing to a previously serialized instance).

Correspondingly, the readUnshared() method:

reads an "unshared" object from the ObjectInputStream. This method is identical to readObject, except that it prevents subsequent calls to readObject and readUnshared from returning additional references to the deserialized instance obtained via this call.

Consequently, the writeUnshared() and readUnshared() methods are unsuitable for round-trip serialization of data structures that contain reference cycles. 

Consider the following code example:

Code Block
public class Person {
  private String name;
    
  Person() {
    // Do nothing - needed for serialization
  }
    
  Person(String theName) {
    name = theName;
  }
 
  // Other details not relevant to this example
}


public class Student extends Person implements Serializable {
  private Professor tutor;
     
  Student() {
    // Do nothing - needed for serialization
  }
     
  Student(String theName, Professor theTutor) {
    super(theName);
    tutor = theTutor;
  }
     
  public Professor getTutor() {
    return tutor;
  }
}
 
public class Professor extends Person implements Serializable {
  private List<Student> tutees = new ArrayList<Student>();
     
  Professor() {
    // Do nothing - needed for serialization
  }
     
  Professor(String theName) {
    super(theName);
  }
     
  public List<Student> getTutees () {
    return tutees;
  }
     
  /**
   * checkTutees checks that all the tutees
   * have this Professor as their tutor
   */
  public boolean checkTutees () {
    boolean result = true;
    for (Student stu: tutees) {
      if (stu.getTutor() != this) {
         result = false;
         break;
      }
    }
    return result;
  }
}

// ...
 
Professor jane = new Professor("Jane");
Student able = new Student("Able", jane);
Student baker = new Student("Baker", jane);
Student charlie = new Student("Charlie", jane);
jane.getTutees().add(able);
jane.getTutees().add(baker);
jane.getTutees().add(charlie);
System.out.println("checkTutees returns: " + jane.checkTutees());
// Prints "checkTutees returns: true"

Professor and Students are types that extend the basic type Person. A student (that is, an object of type Student) has a tutor of type Professor. A professor (that is, an object of type Professor) has a list (actually, an ArrayList) of tutees (of type Student). The method checkTutees() checks whether all of the tutees of this professor have this professor as their tutor, returning true if that is the case and false otherwise. 

Suppose that Professor Jane has three students, Able, Baker, and Charlie, all of whom have Professor Jane as their tutor. Issues can arise if the writeUnshared() and readUnshared() methods are used with these classes, as demonstrated in the following noncompliant code example.

Noncompliant Code Example

This noncompliant code example does something bad attempts to serialize the data from the previous example using writeUnshared(). 

Code Block
bgColor#FFcccc
String filename = "serial";
try(ObjectOutputStream oos = new ObjectOutputStream(new 
      FileOutputStream(filename))) {
  // Serializing using writeUnshared
  oos.writeUnshared(jane);
} catch (Throwable e) {
  // need some code here
 Handle error
}

// Deserializing using readUnshared
try(ObjectInputStream ois = new ObjectInputStream(new 
      FileInputStream(filename))){
  Professor jane2 = (Professor)ois.readUnshared();
  System.out.println("checkTutees returns: " +
                     jane2.checkTutees());
} catch (Throwable e) {
  // Handle error
}

However, when the data is deserialized using readUnshared(), the checkTutees() method no longer returns true because the tutor objects of the three students are different from the original Professor object.

Compliant Solution

This compliant solution overcomes the problem of the noncompliant code exampleuses the writeObject() and readObject() methods to ensure that the tutor object referred to by the three students has a one-to-one mapping with the original Professor object. The checkTutees() method correctly returns true.

Code Block
bgColor#ccccff
String filename = "serial";
try(ObjectOutputStream oos = new ObjectOutputStream(new 
      FileOutputStream(filename))) {
	// Serializing using writeUnshared
	oos.writeObject(jane);
} catch (Throwable e) {
    // need some code here

...

 Handle error
} 

// Deserializing using readUnshared
try(ObjectInputStream ois = new ObjectInputStream(new 
      FileInputStream(filename))) {
Professor jane2 = (Professor)ois.readObject();
System.out.println("checkTutees returns: " +
                   jane2.checkTutees());
} catch (Throwable e) {
    // Handle error
} 

Applicability

Using the writeUnshared() and readUnshared() methods may be bad.

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

MSC62-J

medium

low

low

P6

L2

Automated Detection

Automated detection is straightforward.

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this guideline on the CERT website.

Bibliography

...

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="dc9150fb-0894-4ef3-96e7-30704be8776c"><ac:plain-text-body><![CDATA[

...

[[API 2006

...

AA. References#API 06]]

...

[Class ObjectOutputStream

...

http://download.oracle.com/javase/6/docs/api/java/io/ObjectOutputStream.html] and [Class ObjectInputStream

produce unexpected results when used for the round-trip serialization of the data structures containing reference cycles.

Bibliography

 

...

Image Added Image Added Image Added

...