alibaba / alibaba/fastjson2

[BUG] fastjson2 短字段名 hash 快路径的 Unsafe 8 字节读取在缓冲区末尾附近存在越界读取

Open
#7,865 0 comments 1 reaction 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.