views:

133

answers:

2

The Effective Java says that readResolve works only if all fields are transient. Isn't this a bug? Why would the Java creators do a such thing?

-- update

Sorry, I mean the More Effective Java, see slide 30.

+1  A: 

I think you misunderstood Josh Bloch. From the java spec:

For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized.

As you can see, here is nothing similar to your statement.

As I know one of the most typical use case for readResolve is when you read serialized singleton (or any object for which you don't want to create a new instance, like manually implemented enumerations i.e. without enum keyword). In readResolve you can read fields (if they exist) and decide which of already created objects (singletons as usual) should be returned instead of creating new instance.

Roman
I'd post the wrong link, see my update please.
Tom Brito
@Tom Brito: I saw your update. Cannot comment it, I've never done such tricky things. If you have sources of ElvisStealer class, post them, it's interesting to see.
Roman
No, just the slide before it.
Tom Brito
A: 

For completeness, slide 29 is:

Item 77: Pop Quiz: Is This Class a Singleton?

public class Elvis implements Serializable {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
    private Object readResolve() {
        return INSTANCE;
    }
}

slide 30 is:

Answer: Unfortunately Not

The first edition oversold the power of readResolve

> Elvis has a nontransient field (favoriteSongs)
> Cleverly crafted attack can save reference to deserialized
   Elvis instance when this field is deserialized

  • See ElvisStealer for details (Item 77)

> readResolve works only if all fields are transient

Item 77 is:

Item 77: For instance control, prefer enum types to readResolve

...

If the Elvis class is made to implement Serializable, the following readResolve method suffices to guarantee the singleton property:

// readResolve for instance control - you can do better!
private Object readResolve() {
  // Return the one true Elvis and let the garbage collector
  // take care of the Elvis impersonator.
  return INSTANCE;
}

This method ignores the deserialized object, returning the distinguished Elvis instance that was created when the class was initialized. Therefore, the serialized form of an Elvis instance need not contain any real data; all instance fields should be declared transient. In fact, if you depend on readResolve for instance control, all instance fields with object reference types must be declared transient. Otherwise, it is possible for a determined attacker to secure a reference to the deserialized object before its readResolve method is run, using a technique that is vaguely similar to the MutablePeriod attack in Item 76.

The attack is a bit complicated, but the underlying idea is simple. If a singleton contains a nontransient object reference field, the contents of this field will be deserialized before the singleton’s readResolve method is run. This allows a carefully crafted stream to “steal” a reference to the originally deserialized singleton at the time the contents of the object reference field are deserialized.

It's not a case of only working if all the fields are transient, it's that it's only safe if all Object references are transient. The slide you reference goes further and says is any fields are non-transient an attacker can grab a reference to the deserialized Object.

As to why: one can argue that Josh Bloch's use of readResolve() for a singleton was an unintended use of the API, one the creators didn't envision when they made it. You could also argue it's simply an unforeseen consequence. I don't think however it was a deliberate weakness.

cletus
Yes, this paragraph is better explained, now looks like a programmes bug, not a (ex-)Sun's.
Tom Brito
I think the Slide I'm seeing wrong change "works secure" by "works only if.."
Tom Brito
@cletus: who Josh Block means when he says 'attacker'? I just don't get the case when someone wants/needs to create "second singleton" in my program.
Roman
@Roman: "attacker" here is a bit of a misnomer as it requires code running in the same VM and at the code level there's really no such thing as code security but it matters for things like implementing the subset of Java for the Google App Engine, which is code run inside the VM that can be viewed as "hostile". So if they used `readResolve()` to ensure a singleton on GAE it would be vulnerable to attack.
cletus
@cletus: thanks!
Roman
It's not really secure if all fields a transient anyway. Avoid singletons. Even avoid making sensitive classes serialisable.
Tom Hawtin - tackline
All this sounds a bit paranoid. As I understand it's enough to avoid one common JVM for me and that bad guy. Until the moment I've succeeded with this more simple task.
Roman
@cletus It's a little more general than that. It's important if the code is ever used in (or inspires code used in) a library available for use where it might be used by mobile code. ("mobile code" - code that is mobile, not (necessarily) code on a phone.)
Tom Hawtin - tackline