最新消息:欢迎访问若有所思,如果你也有与我相同的兴趣爱好,请关注我的站点。

FastJSON序列化特殊字符BUG

问题排查 若远 41874浏览 0评论
||||| 7 |||||

现象描述

所有小于等于1.1.39的版本均有此问题

当序列化的任何一个字符串或者可能成为字符串的变量中包含UNICODE码的一些特殊字符时将会抛出异常导致序列化失败。
抛出以下异常:

异常重现

出现的条件

  1. 待序列化的数据中包含\u0080\u00A0的特殊字符
  2. 且包含2个以上包括2个除此之外的特殊字符,比如\u0001
  3. 调用JSON.toString()

测试代码

以上代码是重现大多数使用场景,其实对简单的可以通过一行语句直接重现错误JSON.toJSONString("\u0001\u0080\u0002");

原因

这里对其中一种数据类型(MAP)的现场进行分析,另外几种类型基本类似可以举一反三(有兴趣的同学可以试一试)。
首先需要抱怨一下FastJSON糟糕的代码质量,全篇几乎无注释非常不友好,GITHUB上单元测试也是凌乱不堪,真是怀疑公司内部有这么多人追捧为的是什么?难道都没看过源码?
进入正题,其实这个BUG的真正原因也就是低下的代码质量,缺乏管理的测试导致的。首先,我们可以通过异常马上定位到异常的代码段

出问题的就是buf[bufIndex++] = replaceChars[(int) ch];这行代码(不得不提一下,这个函数有200多行代码,尼玛)。 进入调试模式,通过观察整个过程的函数栈我们很快就可以掌握整个序列化的处理流程:
fastjson_bug_flow

如上图所示,整个序列化大致可以是:

  1. JSON.toString(),序列化调用的入口
  2. 扫描配置的Featrue选项通过serializer.config设置序列化的方式(比如是否是包含类信息、是否替换特殊字符、是否兼容浏览器显示等等)
  3. serializer.write(object) 开始真正的序列化工作,根据数据类型的不同分别会委派给相应的ObjectSerializer处理(比如Map对应MapSerializer,这里应该学习了Hessian的封装方式,有兴趣的朋友可以比对一下代码)
  4. 遍历待序列化对象的所有Field(如果是简单直接可序列类型,比如String、Integer等那么直接一次写结果就OK相当于一次writeValue),调用对应FieldSerializerwriteValue根据数据类型(也是委派)翻译写入JSON的格式
  5. 通过write方法写入到JSON Stirng的BUFFER,直接引用JSONSerializer中的SerializeWriter(写入动作还有很多的格式化操作)
  6. 返回最终序列化结果

问题就出现在第5步中对写入值的格式化过程中有一项特殊字符的转换操作
fastjosn_bug_debug

如上图所示,FastJSON是通过查表com.alibaba.fastjson.parser.CharTypes.replaceChars的方式来替换不可见的特殊字符为显示UNICODE(即那些\u0001等不会显示为空),但是忽略了80~A0段的空字符处理(\u0080转换为ASCII为128replaceChars的长度为128,边界溢出)。

fastjson_bug_utf8_00

fastjson_bug_utf8_80
上图即位两段特殊字符的对照表,超过02228的特殊字符代码中作了强行过滤所以比较幸运的没有踩到坑。

解决方案

知道了故障发生的原因,修复问题就非常简单。在最新版的1.1.41版本已经修正次问题,建议大家更新

PATCH LINK:https://github.com/alibaba/fastjson/commit/cdf7cb253e961666e2b3c2bdd423abe73ba4324a#diff-0

总结

虽然是一个非常隐晦的BUG受影响面不大,但是由于FASTJSON在淘宝内部使用非常广泛,而FastJson接口中没有显示声明任何要求捕捉的异常导致一般系统在使用时不会对其进行try-catch,那么想象一下如果恶意构造数据导致抛出异常那么可能就会导致数据丢失甚至时系统故障。本站另外一片文章中介绍的另外一个中间件METAQ受这个BUG的影响在特殊状态下会导致消息阻塞无法发送(系统的表现时拒绝服务,LOAD为0)

当然从这里也可以看到字符处理工具往往对特殊字符集的处理容易出现疏漏,所以应该在这类工具中对整个相应的字符集(这里指UNICODE)提供完整的单元测试和回归测试,FASTJSON在这方面做的都时不足的。同时,单元测试代码晦涩、代码几乎没有注释也为后面的BUG埋下了伏笔。

参考资料

  1. UTF-8字符表:http://unicode-table.com/en/
  2. FastJSON开源库:https://github.com/alibaba/fastjson

转载请注明:若有所思-胡磊 » FastJSON序列化特殊字符BUG

GuestGuestGuestGuestGuestGuest若远
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (3)

  1. 赞,非常有帮助
    mayee3年前 (2016-01-15)回复
  2. 有用,谢谢!
    codingwhy2年前 (2017-02-28)回复