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

Cannot deserialize prior written data into field initialized with constant value

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      My understanding is that in general, it should be possible to change the initializer for a final field without breaking the deserialization of previously deserialized data.

      What I'm seeing is that Java serialization injects final fields using Unsafe, overwriting the object reference before returning the object to the user. In most cases, this works fine. I'm unfortunately seeing that if Java thinks the field is constant, field accesses in other code are replaced with the constant value. This means that while the field is technically overwritten by Java serialization, the overwrite is not observable in practice.

      My suspicion is that final fields on types that implement Serializable should _not_ be trusted for inlining, but currently are being by default.

      REGRESSION : Last worked in version 8

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the executable test case twice (runs 0, 1).
      Change the contents of getLatestProtocolVersion to return "1.1", and run again (run 2).
      Inline getLatestProtocolVersion so that the initializer of protocol is 'private final String protocol = "1.1";'. Run again (run 3).

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      On all runs (0, 1, 2, 3) of the program, the string "1.0" is outputted.
      ACTUAL -
      "1.0" is output on runs 0, 1, 2 of the program, "1.1" is output on run 3.

      ---------- BEGIN SOURCE ----------
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.io.Serializable;

      public final class Repro implements Serializable {
          private static final long serialVersionUID = 0;
          private final String protocol = getLatestProtocolVersion();
          private final byte[] data;

          public static String getLatestProtocolVersion() {
              return "1.0";
          }

          public Repro(byte[] data) {
              this.data = data;
          }

          @Override
          public String toString() {
              return protocol;
          }

          public static void main(String[] _args) throws Exception {
              File file = new File("temp");
              if (file.exists()) {
                  try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(file))) {
                      System.out.println(input.readObject().toString());
                  }
              } else {
                  try (ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(file))) {
                      Repro object = new Repro(new byte[0]);
                      output.writeObject(object);
                      System.out.println(object.toString());
                  }
              }
          }
      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      The bug can be bypassed in a few ways. Two are:

      1. Initialize the variable via a method call.
      2. Initialize the variable in a constructor.

      FREQUENCY : always


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

              Created:
              Updated: