[BUG]
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
`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=512`,`System.arraycopy` 越界
```java
// 测试代码
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 结构:
```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" : "
***脱敏-机构名称***资产管***有限公司(以下简称“招租方”)对:***脱敏-城市***市城投江南新城2#楼(***脱敏-城市***市城投)【 10层 ***脱敏-面积***㎡ ***脱敏-城市***市城投(延时的除外)在阿里资产平台进行公开进行,现就有关事项公告如下:
注:2025年4月7日10时起至2025年4月8日10时止(延时的除外)期间,此竞价入口为唯一指定入口!通过其他竞价平台报名或参与竞价均视为无效操作。
一、招租房产:***脱敏-城市***市江南新城华创产业园2#楼(人才公寓)【 10层 ***脱敏-面积***㎡ 】 、【 11层***脱敏-面积***㎡ 】房屋租赁。
起租价:***脱敏-金额***元/年,保证金:***脱敏-金额***元,加价幅度:***脱敏-金额***元或整倍数,租赁期限:***脱敏-年限***年 ,租金支付方式:一年一付,履约保证金:***脱敏-金额***元,房产用途:以《租赁合同》为准,是否可以联合承租:否。
二、房产概况:***脱敏-城市***市江南新城华创产业园2#楼(人才公寓)【 10层 ***脱敏-面积***㎡ 】 、【 11层***脱敏-面积***㎡ 】房屋。房屋建筑面积:***脱敏-面积***㎡,权利性质:国有,用途:其他,房屋结构:混合,租赁情况:闲置,抵押情况:未知。
三、特别告知:
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 行:
```java
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` 容量检查:
```java
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` 影响
Contributor guide
Research direction
Start at JSONReaderUTF16.readString() around lines 3254-3259 and reproduce the failure through JSON.parseObject(String) using the two-field JSON example. Verify the existing UTF16 path handles a later escaped, over-2000-character field after a shorter escaped field without throwing ArrayIndexOutOfBoundsException; completion is successful parsing of that input.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100