Skip to content

[BUG] #7646

@liuc-git

Description

@liuc-git

问题描述

JSON.parseObject(String) 解析含超长字符串字段的 JSON 时,JSONReaderUTF16.readString() 中实例级重用缓冲区 strBuf 未做容量检查,导致 System.arraycopy 越界抛出 ArrayIndexOutOfBoundsException

环境信息

  • OS信息: macOS 14.2 (M1 pro)
  • JDK信息: Openjdk 21
  • 版本信息: Fastjson2 2.0.61

重现步骤

  1. 构造如下 JSON,其中包含两个关键字段:
    • attachment:值为含 \" 转义的短字符串(在 readString() 中首次分配 strBuf = char[512]
    • noticeDetails:值为超长富文本(>2000字符)且中间含 \" 转义
  2. 调用 JSON.parseObject(jsonString) 解析
  3. 解析到 noticeDetails 字段时,stroff=2328 > strBuf.length=512System.arraycopy 越界
// 测试代码
public class Test {
    public static void main(String[] args) throws Exception {
        String jsonString = new String(Files.readAllBytes(Paths.get("/path/to/json内容.json")));
        JSONObject jsonObject = JSON.parseObject(jsonString);
        System.out.println(jsonObject);
    }
}

测试 JSON 结构:

{
  "attachment" : "{\"files\":[{\"code\":\"200\",\"contentType\":\"application/msword\",\"id\":\"***ENCRYPTED_ID***\",\"name\":\"3岁以下婴幼儿健康养育照护指南(试行).doc\",\"url\":\"v1/file-store/download/***ENCRYPTED***\",\"size\":\"***ENCRYPTED***\"}],\"notes\":\"\",\"attachmentItemMapping\":\"\"}",
  "noticeDetails" : "<p>***脱敏-机构名称***资产管***有限公司(以下简称“招租方”)对:***脱敏-城市***市城投江南新城2#楼(***脱敏-城市***市城投)【 10层 ***脱敏-面积***㎡ ***脱敏-城市***市城投(延时的除外)在阿里资产平台进行公开进行,现就有关事项公告如下:<br>注:2025年4月7日10时起至2025年4月8日10时止(延时的除外)期间,此竞价入口为唯一指定入口!通过其他竞价平台报名或参与竞价均视为无效操作。<br>一、招租房产:***脱敏-城市***市江南新城华创产业园2#楼(人才公寓)【 10层 ***脱敏-面积***㎡ 】 、【 11层***脱敏-面积***㎡ 】房屋租赁。<br>起租价:***脱敏-金额***元/年,保证金:***脱敏-金额***元,加价幅度:***脱敏-金额***元或整倍数,租赁期限:***脱敏-年限***年 ,租金支付方式:一年一付,履约保证金:***脱敏-金额***元,房产用途:以《租赁合同》为准,是否可以联合承租:否。<br>二、房产概况:***脱敏-城市***市江南新城华创产业园2#楼(人才公寓)【 10层 ***脱敏-面积***㎡ 】 、【 11层***脱敏-面积***㎡ 】房屋。房屋建筑面积:***脱敏-面积***㎡,权利性质:国有,用途:其他,房屋结构:混合,租赁情况:闲置,抵押情况:未知。<br>三、特别告知:<br>1.房屋按现状出租,出租前的水、电、物业、包烧等一切费用由招租方当场结清,承租期间的水、电、包烧等一切费用由承租人承担并按实结算。target=\"_blank\">"
}

期待的正确结果

正常解析完成,不抛出异常。

相关日志输出

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: arraycopy: last destination index 2328 out of bounds for char[512]
    at java.base/java.lang.System.arraycopy(Native Method)
    at com.alibaba.fastjson2.JSONReaderUTF16.readString(JSONReaderUTF16.java:3259)
    at com.alibaba.fastjson2.JSONReader.read(JSONReader.java:3544)
    at com.alibaba.fastjson2.JSON.parseObject(JSON.java:476)
    at com.bosssoft.asset.Test.main(Test.java:18)

根因分析

JSONReaderUTF16.readString() 第 3254-3259 行:

char[] strBuf = this.strBuf;
if (strBuf == null) {
    strBuf = new char[stroff + 512];
    this.strBuf = strBuf;
}
System.arraycopy(chars, start, strBuf, 0, stroff);  // 缺少容量校验

strBuf 是实例级重用缓冲区。当处理 attachment 这样含 \ 的短字段时,stroff 很小(如 0),分配 char[512]。后续处理 noticeDetails 这样含 \ 的超长字段时,快速扫描已累积 stroff=2328,但 strBuf 非 null 不会重新分配,arraycopy 直接将 2328 个字符写入 char[512] 导致越界。

修复方案:在 System.arraycopy 前增加 strBuf 容量检查:

char[] strBuf = this.strBuf;
if (strBuf == null || stroff > strBuf.length) {
    strBuf = new char[Math.max(stroff + 512, strBuf.length * 2)];
    this.strBuf = strBuf;
}
System.arraycopy(chars, start, strBuf, 0, stroff);

附加信息

  • 使用 JSON.parseObject(byte[])(如 Files.readAllBytes)可绕过此问题,因 byte 路径使用 JSONReaderUTF8,不受 JSONReaderUTF16.strBuf 影响

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions