专业的JAVA编程教程与资源

网站首页 > java教程 正文

试验java反序列化炸弹碰到的一个HashSet问题

temp10 2024-11-04 14:06:22 java教程 16 ℃ 0 评论

最近在看effective java看到一个反序列化炸弹,使用代码验证了下:

先写一个函数将对象序列化到文件中:

试验java反序列化炸弹碰到的一个HashSet问题

public static void saveObject(Object object,String file) throws Exception {

ObjectOutputStream out = null;

FileOutputStream fout = null;

try {

fout = new FileOutputStream(file);

out = new ObjectOutputStream(fout);

out.writeObject(object);

} finally {

fout.close();

out.close();

}

}

再写一个函数从文件中读取数据反序列化一个对象

public static Object readObject(String file) throws Exception {

ObjectInputStream in = null;

FileInputStream fin = null;

try {

fin = new FileInputStream(file);

in = new ObjectInputStream(fin);

Object object = in.readObject();

return object;

} finally {

fin.close();

in.close();

}

}

主函数如下,先构造一个对象,然后把这个对象序列化到文件中,在从文件反序列化对象:

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

// TODO Auto-generated method stub

String file = "/Users/zhaoyc/10101010101.txt";

Bomb1 bomb = new Bomb1();

saveObject(bomb,file);

readObject(file);

}

对象的构造过程如下:

class Bomb implements Serializable{


private Set<Object> root;

public Bomb() {

root = new HashSet<>();

Set<Object> s1 = new HashSet<>();

Set<Object> s2 = new HashSet<>();

root.add(s1);

root.add(s2);

for(int i=0;i<100;i++) {

Set<Object> t1 = new HashSet<>();

Set<Object> t2 = new HashSet<>();

t1.add("bom");

s1.add(t1);

s1.add(t2);

s2.add(t1);

s2.add(t2);

s1 = t1;

s2 = t2;

}

}

}

这时候程序会一直运行,过段时间CPU飙升



,到底发生了什么?问题在于,反序列化 HashSet 实例需要计算其元素的散列码,这个例子中会计算多少次呢?构造的对象如下:


相当于计算了2的100次方,这已经是一个天文数字了

每次左边的集合中都放一个string 的图画出来感觉不美观,我又换了中对象生成方式,代码如下:

root = new HashSet<>();

Set<Object> s1 = new HashSet<>();

Set<Object> s2 = new HashSet<>();

System.out.println(s1.hashCode() == s2.hashCode());

System.out.println(s1.equals(s2));

System.out.println((new HashSet<String>()).equals(new HashSet<Integer>()));

root.add(s1);

root.add(s2);

for(int i=0;i<100;i++) {

Set<Object> t1 = new HashSet<>();

Set<Object> t2 = new HashSet<>();

s1.add(t1);

s1.add(t2);

s2.add(t1);

s2.add(t2);

s1 = t1;

s2 = t2;

}

s1.add("bom1");

s1.add("bom2");

s2.add("bom1");

s2.add("bom2");

生成的对象就行下面这样子:


然而运行后很快就结束了,难道反序列话的炸弹无效了么,试着调试了下这个对象的结构是这样子的:

[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[bom2, bom1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

原来如此,其实这时候构造的对象是下面这样子的:


明明每次循环都是new了两个set的,为什么出现了一个呢?如下的代码:


仔细思考了下,感觉问题应该还是处在set的hashcode和equals上,试着运行了一下代码:

System.out.println(new HashSet().equals(new HashSet()));

System.out.println(new HashSet().hashCode() == new HashSet().hashCode());

不出所料结果都是true

跟踪了下源码发下其实在hashset的父类AbstractSet中重写了这两个方法:

public int hashCode() {

int h = 0;

Iterator<E> i = iterator();

while (i.hasNext()) {

E obj = i.next();

if (obj != null)

h += obj.hashCode();

}

return h;

}


public boolean equals(Object o) {

if (o == this)

return true;


if (!(o instanceof Set))

return false;

Collection<?> c = (Collection<?>) o;

if (c.size() != size())

return false;

try {

return containsAll(c);

} catch (ClassCastException unused) {

return false;

} catch (NullPointerException unused) {

return false;

}

}


如果仔细观察他们的实现会发现如果连个set的元素个数为空,那么他们的equals是返回true的,而且hashcode恒等于0,那如果是范型呢,试了下,如下:

System.out.println(new HashSet<String>().equals(new HashSet<Integer>()));

结果仍旧是true

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表