Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8362101

java.io.ObjectOutputStream::writeObject successfully after close

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      When ObjectOutputStream.writeObject() is called on a stream that has already been closed, it is expected to immediately throw an IOException. However, if the object being serialized has a custom writeObject method, this check is bypassed. The stream proceeds to execute the custom writeObject method, which can lead to defaultWriteObject() successfully writing data to the closed stream, corrupting its contents. The expected IOException is never thrown, breaking the ObjectOutputStream contract and leading to unpredictable application behavior and data corruption.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Catch IOException on the last writeObject.
      ACTUAL -
      Write successful


      [-84, -19, 0, 5, 115, 114, 0, 1, 65, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 1, 73, 0, 4, 118, 97, 114, 49, 120, 112, 0, 0, 0, 0, 120]
      2
      [-84, -19, 0, 5, 115, 114, 0, 1, 65, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 1, 73, 0, 4, 118, 97, 114, 49, 120, 112, 0, 0, 0, 0, 120, 115, 114, 0, 1, 65, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 1, 73, 0, 4, 118, 97, 114, 49, 120, 112, 0, 0, 0, 0, 120]
      Exception in thread "main" java.lang.RuntimeException: Failed to throw IOException after stream close
      at WriteObjectMemory.main(WriteObjectMemory.java:57)


      ---------- BEGIN SOURCE ----------
      import java.io.*;
      import java.util.Arrays;
      import java.util.HashSet;

      class A implements Serializable {
          private static final long serialVersionUID = 1L;

          static HashSet<A> writeObjectExtent = new HashSet<>();

          private int var1;

          private void writeObject(ObjectOutputStream out) throws IOException {

              out.defaultWriteObject();
              if (writeObjectExtent.contains(this)) {
                  throw new InvalidObjectException("writeObject: object " +
                                                   this.toString() + " has already "
                                                   + "been serialized and should " +
                                                   "have be serialized by reference.");
              } else {
                  writeObjectExtent.add(this);
              }
          }

          A() {
          }

          public void setVar1(int var1) {
              this.var1 = var1;
          }
      }

      public class WriteObjectMemory {
          public static void main(String args[])
              throws IOException
          {
              ByteArrayOutputStream baos = new ByteArrayOutputStream(3000);
              ObjectOutputStream out =
                  new ObjectOutputStream(baos);


              A newObj = new A();
              out.writeObject(newObj);
              if (!A.writeObjectExtent.contains(newObj) || A.writeObjectExtent.size() != 1) {
                  throw new RuntimeException("New object not recorded: expected size=1, actual="
                                            + A.writeObjectExtent.size());
              }

              out.close();
              var arr1 = baos.toByteArray();
              System.out.println(Arrays.toString(arr1));
              try {
                  out.writeObject(new A());
                  System.out.println(A.writeObjectExtent.size());
                  var arr2 = baos.toByteArray();
                  System.out.println(Arrays.toString(arr2));
                  throw new RuntimeException("Failed to throw IOException after stream close");
              } catch (IOException e) {
                  System.out.println("Caught expected IOException after close: " + e.getMessage());
              }
          }
      }

      ---------- END SOURCE ----------

            rriggs Roger Riggs
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: