Mantis - Hessian
Viewing Issue Advanced Details
4069 crash always 06-04-10 05:49 07-18-23 02:04
raggatt2000  
 
normal  
new 4.0.7  
open  
none    
none  
0004069: IndexOutOfBoundsException when deserializing Google Collections
I?m getting an issue when deserializing classes containing Google Collections classes - Hessian throws an IndexOutOfBoundsException when calling readObject() on the Hessian2Input instance. I?ve attached an example class which replicates the exception. I?m running using the following:

Java 6 (jdk 1.6.0_17)
Google Collections 1.0
Hessian 4.0.7 - (note: also affects 4.0.x)

As far as I can tell, the exception seems to occur as a result of one of/a combination of the following:
- ImmutableMap implements custom serialization methods writeReplace() and readResolve()
- A single instance being referenced from multiple locations

Resulting exception is:

Exception in thread "main" com.caucho.hessian.io.HessianFieldException: com.ml.HessianTest.test: com.ml.HessianTest cannot be assigned from null
    at com.caucho.hessian.io.UnsafeDeserializer.logDeserializeError(UnsafeDeserializer.java:774)
    at com.caucho.hessian.io.UnsafeDeserializer$ObjectFieldDeserializer.deserialize(UnsafeDeserializer.java:421)
    at com.caucho.hessian.io.UnsafeDeserializer.readObject(UnsafeDeserializer.java:239)
    at com.caucho.hessian.io.UnsafeDeserializer.readObject(UnsafeDeserializer.java:150)
    at com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2188)
    at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2109)
    at com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:114)
    at com.caucho.hessian.io.SerializerFactory.readMap(SerializerFactory.java:522)
    at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2079)
    at com.ml.HessianTest.main(HessianTest.java:51)
Caused by: java.lang.IndexOutOfBoundsException: Index: 80, Size: 73
    at java.util.ArrayList.RangeCheck(ArrayList.java:547)
    at java.util.ArrayList.get(ArrayList.java:322)
    at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1790)
    at com.caucho.hessian.io.UnsafeDeserializer$ObjectFieldDeserializer.deserialize(UnsafeDeserializer.java:417)
    ... 8 more

Java code for replicating below:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Map;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

public class HessianTest implements Serializable {

    private final Map<String, String> map;
    private final HessianTest test;

    private HessianTest(Map<String, String> map, HessianTest test) {
        this.map = map;
        this.test = test;
    }

    public static void main(String[] args) throws IOException {

        Map<String, HessianTest> map = Maps.newHashMap();
        HessianTest lastTest = null;
        //seems to start failing when limit set to 18
        for (int i = 0; i < 18; i++) {
            HessianTest test = new HessianTest(ImmutableMap.of("id" + i, "id" + i), lastTest);
            //following line works fine - probably indicating
            //that the issue is with the google collection
            //serialization routine
            //HessianTest test = new HessianTest(Collections.singletonMap("id" + i, "id" + i), lastTest);
            map.put("id" + i, test);
            lastTest = test;
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output oos = new Hessian2Output(baos);

        oos.writeObject(map);
        oos.close();
        baos.close();

        byte[] input = baos.toByteArray();

        ByteArrayInputStream bais = new ByteArrayInputStream(input);
        Hessian2Input ois = new Hessian2Input(bais);

        ois.readObject();

    }

    private static final long serialVersionUID = 1L;

}
 HessianTest.java [^] (1,605 bytes) 06-04-10 05:49
 IdentityIntMap.java [^] (6,809 bytes) 06-08-10 11:24

Notes
(0004636)
raggatt2000   
06-08-10 11:22   
I've found the bug which is causing this issue. It's in the put method of the IdentityIntMap class.

The current implementation of the remove method only virtually removes the Object from the map. Instead of nulling out the reference which which is being removed, it is allocated a reference value of -1. All is fine until the map grows too large and needs to be rebalanced. The put method is called to reinsert all the objects back into the new larger map and each object is counted again when size is incremented. To fix, the following line:

_size++;

should be changed to:

if (value != -1)
    _size++;

The deleted object will then be ignored by the put method.

I'll attach the suggested fixed class to this issue.

Thanks
(0004973)
Aaron Pieper   
01-19-11 12:18   
Thank you, this fix worked for me.

A similar error can be caused if you try to deserialize a mangled inputstream. For example, I converted a byte array to a string using an invalid string encoding. When I tried to deserialize the resulting string input stream using Hessian, it threw an IndexOutOfBoundsException in the same place.
(0005273)
alexeyre   
05-22-11 06:55   
I've had the same issue with a Hessian service which returns an ArrayList or object which contain fields of type org.w3c.Node.
The fix provided in this bug worked for me too.

Will this bug-fix be released sometime soon ?