先一段简单的测试代码,看看fast-serialization在反序列化时缺少数据完整性检查是什么样一个情况:
//对字符串Hello World进行反序列化
String str = "Hello World !";
FSTConfiguration conf = FSTConfiguration.getDefaultConfiguration();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
conf.getObjectOutput(outStream).writeObject(str);
byte[] bytes = outStream.toByteArray();
//此处模拟数据失真,故意只截取序列化结果中的一部分数据
byte[] copy = new byte[bytes.length - 5];
System.arraycopy(bytes, 0, copy, 0, copy.length);
//对失真的序列化结果进行反序列化,反序列化过程中并没有抛出异常
ByteArrayInputStream inStream = new ByteArrayInputStream(copy);
str = (String) conf.getObjectInput(inStream).readObject();
System.out.println(str);
以上代码输出结果为“Hello Wo ”。
咋一看似乎一切正常,因为故意截取了5个字节,所以反序列化的输出结果中丢失了5个字符,这样反序列化后的结果就失真了。
可仔细想想就发现不对的地方,如果是丢失了5个字节的话,输出结果应该是“Hello Wo”-8个字符,而不是“Hello Wo+5个空格“-13个字符,既然FST知道序列化时字符串length是13个字符,那为何没有因为反序列化的字节数据不完整而抛出异常,反而输出了一个失真的结果呢?
以上问题,最终在de.ruedigermoeller.serialization.util.FSTInputStream和de.ruedigermoeller.serialization.FSTObjectInput类中找到了答案:
public final class FSTInputStream extends InputStream {
public int chunk_size = 1000;
... ...
public void initFromStream(InputStream in) {
try {
this.in = in;
if (buf==null) {
buf = cachedBuffer.get();
if ( buf == null ) {
buf = new byte[chunk_size];
cachedBuffer.set(buf);
}
}
int read = in.read(buf);
count+=read;
//1.将所有反序列化的字节数据读取到本地的buffer中
while( read != -1 ) {
try {
//实际字节数据大小超出buffer容量时,buffer扩充到目前的2倍,默认是每次扩充1000个字节
//所以buffer中的字节数据并不都是有效数据
if ( buf.length < count+chunk_size ) {
ensureCapacity(buf.length*2);
}
read = in.read(buf,count,chunk_size);
if ( read > 0 )
count += read;
} catch ( IndexOutOfBoundsException iex ) {
read = -1; // many stream impls break contract
}
}
in.close();
} catch (IOException e) {
FSTUtil.rethrow(e);
}
}
... ...
}
public class FSTObjectInput extends DataInputStream implements ObjectInput {
... ...
FSTInputStream input;
... ...
public String readStringUTF() throws IOException {
//2.获取反序列化的字符串长度
int len = readCInt();
char[] charBuf = getCharBuf(len*3);
ensureReadAhead(len * 3);
byte buf[] = input.buf;
int count = input.pos;
int chcount = 0;
//3.以下是对字符串进行反序列化,首先从Input stream的buffer中读取到字符串的bytes数组,
for (int i = 0; i < len; i++) {
//这里从buffer中获取字节时并没有判断是否已经超出有效字节数据的范围
//这就解释了为什么测试代码反序列化的结果是“Hello Wo ”,是因为拿到无效的字节数据
char head = (char) ((buf[count++] + 256) &0xff);
if (head < 255) {
charBuf[chcount++] = head;
} else {
int ch1 = ((buf[count++] + 256) &0xff);
int ch2 = ((buf[count++] + 256) &0xff);
charBuf[chcount++] = (char) ((ch1 << 8) + (ch2 << 0));
}
}
input.pos = count;
return new String(charBuf, 0, chcount);
}
... ...
}