gaob13 / kryo

Automatically exported from code.google.com/p/kryo
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Serializes items that can't be deserialized #33

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Register a number of classes with Kryo, including 'Class.class'
2. Serialize something containing a Class as a field with Kryo
3. Deserialize with Kryo

What is the expected output? What do you see instead?
We'd expect that it would fail serializing given that it won't be able to 
deserialize due to the 'newInstance' constraint w/ Class.  Instead it 
serializes just fine, but a VM with the same registration properties as the 
current one can't deserialize it.

What version of the Kryo are you using?
Latest

Please provide any additional information below.

Original issue reported on code.google.com by rbakhru@gmail.com on 20 Dec 2010 at 5:23

GoogleCodeExporter commented 8 years ago
I'm not seeing this behavior. The following fails with a 
SerializationException, as expected. Please provide a simple code snippet 
showing the problem and I will reopen this bug.

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoTestCase;
public class SerializeClassTest extends KryoTestCase {
    public void testFieldSerializer () {
        Kryo kryo = new Kryo();
        kryo.register(TestClass.class);
        TestClass object1 = new TestClass();
        kryo.writeClassAndObject(buffer, object1);
        buffer.flip();
        TestClass object2 = (TestClass)kryo.readClassAndObject(buffer);
        System.out.println(object2.c);
    }
    static public class TestClass {
        public Class c;
    }
}

Original comment by nathan.s...@gmail.com on 20 Dec 2010 at 8:05

GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago
Here's an example -- it succeeded in serializing but failed in deserializing, 
which is fairly dangerous

Thanks!

Succeeded in serializing to 3 bytes
Exception in thread "main" com.esotericsoftware.kryo.SerializationException: 
Unable to deserialize object of type: KryoTest$TestClass
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:563)
    at com.esotericsoftware.kryo.ObjectBuffer.readClassAndObject(ObjectBuffer.java:202)
    at KryoTest.main(KryoTest.java:38)
Caused by: com.esotericsoftware.kryo.SerializationException: Error constructing 
instance of class: java.lang.Class
Serialization trace:
_containedClass (KryoTest$TestClass)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:681)
    at com.esotericsoftware.kryo.Serializer.newInstance(Serializer.java:75)
    at com.esotericsoftware.kryo.serialize.FieldSerializer.readObjectData(FieldSerializer.java:192)
    at com.esotericsoftware.kryo.Serializer.readObject(Serializer.java:61)
    at com.esotericsoftware.kryo.serialize.FieldSerializer.readObjectData(FieldSerializer.java:220)
    at com.esotericsoftware.kryo.serialize.FieldSerializer.readObjectData(FieldSerializer.java:192)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:558)
    ... 2 more
Caused by: java.lang.IllegalAccessException: Can not call newInstance() on the 
Class for java.lang.Class
    at java.lang.Class.newInstance0(Class.java:320)
    at java.lang.Class.newInstance(Class.java:308)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:668)
    ... 8 more

Java code:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.ObjectBuffer;

/**
 * 
 * @author rbakhru
 */
public class KryoTest {
    private static Logger LOG = LoggerFactory.getLogger(KryoTest.class);

    public static class TestContainedClass {
    }

    public static class TestClass {
        private final Class<?> _containedClass = TestContainedClass.class;
    }

    public static void main(String[] args) throws Exception {
        Kryo kryo = new Kryo();
        kryo.register(TestClass.class);
        kryo.register(Class.class);

        ObjectBuffer buffer = new ObjectBuffer(kryo, 1024, 1024 * 1024);

        TestClass object1 = new TestClass();
        byte[] bytes = buffer.writeClassAndObject(object1);

        System.out.println("Succeeded in serializing to " + bytes.length + " bytes");

        buffer = new ObjectBuffer(kryo, 1024, 1024 * 1024);
        TestClass returnVal = (TestClass) buffer.readClassAndObject(bytes);

        System.out.println(returnVal);

    }

}

Original comment by rbakhru@gmail.com on 20 Dec 2010 at 11:49

GoogleCodeExporter commented 8 years ago
Ah, I see. The problem is here:
kryo.register(Class.class);
This registers the class "Class" with the default serializer (FieldSerializer). 
This is invalid, as FieldSerializer cannot create instances of Class. The fix 
is:
kryo.register(Class.class, new ClassSerializer(kryo));

Currently FieldSerializer calls Serializer#newInstance which calls 
Kryo#newInstance. Either of these newInstance methods can be overridden. This 
means currently the only way to know if a class can be instantiated is to call 
one of these methods, which is somewhat wasteful.

FieldSerializer knows the type it needs to handle in its constructor and could 
call newInstance there and throw away the response. This object creation may be 
unexpected, but I guess could be documented. Calling a method intended for 
overriding from the constructor will cause the subclass' method to be invoked 
before the subclass constructor is run -- unexpected behavior that may cause 
initialization problems. This could just be accepted, or it could be worked 
around with a boolean that causes newInstance to happen only the first time 
writeObjectData is called. The question is: Is it really worth the trouble 
given the (admittedly somewhat minor) drawbacks?

Original comment by nathan.s...@gmail.com on 20 Dec 2010 at 11:54

GoogleCodeExporter commented 8 years ago
In Kryo v2, object creation for FieldSerializer and other generic serializers 
typically occurs in Serializer#create(...). This method could be overridden and 
could potentially read from the input before creating an instance. This means 
there is no good place to check if the type can be constructed until 
deserialization actually occurs.

I recommend using application specific code to verify instances of a type can 
be constructed in order to catch the error before deserialization. To test if 
construction is possible using the default mechanism, first call Kryo#register, 
then call Kryo#newInstance.

Original comment by nathan.s...@gmail.com on 26 Apr 2012 at 2:28