[alibaba/fastjson](已提供可复现的源码,求fix)fastjson无法反序列化超出某种限制的类

2025-10-28 646 views
3

几点说明:

  1. 我所在的项目密级很高,所以我需要项目相关的信息打码,抱歉更无法提供项目源码(更新,已经提供了一份脱敏的源码)
  2. 我会尽量用描述的方式描述清楚问题,并给出自己的分析过程

环境: java 8 fastjson 1.2.61(1.2.58也试过)

背景: 我们有一个类SkDto.java,这个类的字段比较多,且含有一些内部static类。而且我们用lombok标注它。 我们用这种方式跑单测: str = "{}"; // 原始值不是{},此处简化下,因为{}也能复现 SkDto skDto= JSON.parseObject(str, SkDto.class);

在一次升级之后,我们增加了几个字段之后,这一行代码无法通过单测。 报错是: testcase XXXXXXXXXXXXXXX > testDTO: java.lang.VerifyError: (class: com/alibaba/fastjson/parser/deserializer/FastjsonASMDeserializer_4_SkDTO, method: deserialze signature: (Lcom/alibaba/fastjson/parser/DefaultJSONParser;Ljava/lang/reflect/Type;Ljava/lang/Object;I)Ljava/lang/Object;) Illegal target of jump or branch

我们给SkDTO.java删字段,发现删除long类型的一个成员变量之后,仍然无法工作。但是删除一个Arraylist类型的成员变量 或者随便多删除几个成员变量之后,即可工作。所以猜测我们是超出了某个大小限制,只有把类缩减到这个限制以下之后,才能工作。 这个限制不是简单的数量限制,因为删除一个long类型的成员变量 和删除一个Arraylist类型的成员变量 的结果不同。

开始验证猜测:

分别打开两个IDE,一个IDE用有问题的SkDTO.java,一个IDE用“删除一个Arraylist类型的成员变量 ”之后的SkDTO.java。

JSON.java 560行:return parseObject(text, clazz, new Feature[0]); 调用了

JSON.java 383行:T value = (T) parser.parseObject(clazz, null); 调用了

DefaultJSONParser.java 673行: ObjectDeserializer deserializer = config.getDeserializer(type); 调用了

ParseConfig.java 411行:return getDeserializer((Class<?>) type, type); 调用了 ParseConfig.java 678行:deserializer = createJavaBeanDeserializer(clazz, type); 调用了

ParseConfig.java 840行:return asmFactory.createJavaBeanDeserializer(this, beanInfo); 调用了

ASMDeserializerFactory.java 89-90行: Class<?> deserClass = classLoader.defineClassPublic(classNameFull, code, 0, code.length); Constructor<?> constructor = deserClass.getConstructor(ParserConfig.class, JavaBeanInfo.class);

在此之前,两个IDE的debug都看不出来明显区别,在这里出现了完全不同的反应:

其中一个IDE在90行抛出了异常。

分别在两个IDE的第90行打断点: 比较两个class,发现两个ASM类有明显不同,一个有构造函数,一个没有: 2 看构造的过程,都是ClassLoader的,唯一区别就是传入的参数: 3

本行之上都是描述事实,本行之下是猜想内容:

  1. 当类大小比较大的时候,或者触发了某个条件,ASM生成的字节码不正常。
  2. 当一个类的大小超过64K时候,用protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)生成的类和ASMSerializerFactory配合的不好;

我查了不少资料,我和这位朋友的状态非常相似: https://github.com/alibaba/fastjson/issues/1207

和这个帖子里面的评论的朋友也有一些相似,但是不完全相同: https://github.com/alibaba/fastjson/issues/1092 我的症状是只能减字段才能解决,不能加字段;他的症状是增减字段都能解决

回答

4

本地没重现,希望能提供编码后的testcase,如果项目机密,类名和字段名都可以使用字母+数字代替

4

是Oracle JDK么?

6

java -version

java version "1.8.0_211" Java(TM) SE Runtime Environment (build 1.8.0_211-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

8

本地没重现,希望能提供编码后的testcase,如果项目机密,类名和字段名都可以使用字母+数字代替

明白。但是项目确实非常机密。我准备全凭印象自己构造一个类似的DTO然后提供。

6

你好,我反复上传都无法在这里上传代码。所以把代码和测试贴在这里了: https://github.com/zhaiyao/codeman/blob/master/java/thirdparty/fastjson/AbcDTO.java

对于这个DTO来说,临界值是63个(100-162)。只要删掉 list163 就能工作。 Java版本已经提供。 其他版本如下: compileOnly "org.projectlombok:lombok:1.18.2" compile "com.alibaba:fastjson:1.2.58"

5

@wenshao 请问用我提供的Dto可以复现吗?

2

@zhaiyao 建议你先用 fieldBase 模式.这个方式不会用到 Asm

9

Fixed in #2858

1

@blindpirate 辛苦

4

在1.2.79版本 ,当类的Arraylist类型的成员变量超过140个的时候还是会出现 2779 ASM生成的跳转目标可能溢出异常。