[BUG] fastjson2 短字段名 hash 快路径的 Unsafe 8 字节读取在缓冲区末尾附近存在越界读取
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
fastjson2 在解析短字段名并进行 typed bean binding 时,字段名 hash 快路径会通过 `Unsafe` 一次性读取 8 字节 little-endian `long`。当字段名靠近输入 byte array 结尾、从读取位置到缓冲区末尾不足 8 字节时,该读取会越过输入缓冲区边界。
该问题可以通过 Jazzer `UnsafeSanitizer` 稳定复现。
代表性输入:
```json
{"ab":1}
```
确认复现版本:`2.0.61`, `2.0.62`, `2.0.63`, `2.0.64`, `2.0.65`。
### 环境信息
- OS信息:Linux x86_64
- JDK信息:Temurin OpenJDK 17.0.11
- 版本信息:Fastjson2 2.0.61, 2.0.62, 2.0.63, 2.0.64, 2.0.65
### 重现步骤
1. 使用 `JSON.parseObject(String, Class)` 方法进行 typed bean binding。
2. 输入短字段名 JSON 数据:`{"ab":1}`。
3. 使用 Jazzer `UnsafeSanitizer` 运行时,出现 `FuzzerSecurityIssueCritical: Access at offset 18 with size 8 exceeds end offset 24`。
```java
import com.alibaba.fastjson2.JSON;
public class Reproducer {
public static class TestBean {
public long id;
public int count;
public double ratio;
public boolean enabled;
public String name;
public java.math.BigDecimal amount;
public java.util.List tags;
public java.util.Map attributes;
}
public static void main(String[] args) {
JSON.parseObject("{\"ab\":1}", TestBean.class);
}
}
```
在普通 JVM 下,上述代码通常会正常执行;需要使用 Jazzer `UnsafeSanitizer` 才能观察到越界读取。复现命令示例:
```bash
JAVA_HOME=/path/to/jdk17 ./run-bug01-unsafe-read.sh
```
其中 `run-bug01-unsafe-read.sh` 使用 Jazzer 启用 sanitizer,并执行上述 typed bean 解析路径。
### 期待的正确结果
解析器不应读取输入 byte array 边界之外的数据。
在字段名 hash 快路径中,如果当前位置到输入缓冲区末尾不足 8 字节,应当使用有边界检查的读取方式,或者回退到 byte-wise 的安全慢路径,而不是直接调用 `Unsafe` 读取完整 8 字节。
### 相关日志输出
*请复制并粘贴任何相关的日志输出。*
Jazzer `UnsafeSanitizer` 报告如下:
```text
com.code_intelligence.jazzer.api.FuzzerSecurityIssueCritical:
Access at offset 18 with size 8 exceeds end offset 24
at com.code_intelligence.jazzer.sanitizers.UnsafeSanitizer.report(UnsafeSanitizer.java:428)
at com.code_intelligence.jazzer.sanitizers.UnsafeSanitizer.checkAccess(UnsafeSanitizer.java:505)
at com.code_intelligence.jazzer.sanitizers.UnsafeSanitizer.checkByteSizedAccess(UnsafeSanitizer.java:444)
at com.code_intelligence.jazzer.sanitizers.UnsafeSanitizer.primitiveGetterHook(UnsafeSanitizer.java:263)
at com.alibaba.fastjson2.util.IOUtils.getLongLE(IOUtils.java:2776)
at com.alibaba.fastjson2.util.Fnv.hashCode64(Fnv.java:82)
at com.alibaba.fastjson2.JSONReaderASCII.readFieldNameHashCode(JSONReaderASCII.java:227)
at com.alibaba.fastjson2.reader.ORG_1_8_TestBean.readObject(Unknown Source)
at com.alibaba.fastjson2.JSON.parseObject(JSON.java:992)
```
建议修复方式:
- 在调用 `IOUtils.getLongLE` 前检查剩余长度,确保 `offset + 8 <= end`;
- 或者当字段名长度较短、输入尾部不足 8 字节时,使用 byte-wise 安全拼装逻辑;
- 添加回归测试,覆盖字段名位于输入末尾附近、剩余字节不足 8 的情况。
Contributor guide
Research direction
Start at JSON.parseObject(String, Class) and trace the typed-binding path through JSONReaderASCII.readFieldNameHashCode, Fnv.hashCode64, and IOUtils.getLongLE. Run run-bug01-unsafe-read.sh with the {"ab":1} input, then add regression coverage for field names near the input end and verify Jazzer UnsafeSanitizer reports no out-of-bounds read.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100