CVE-2026-39414:S3 Select 超大记录与 SIMD 绕过

第一轮给 CSV 与 JSON Lines 加上 1 MiB 上限,第二轮又发现 SIMD fast path 完全绕过了它。

状态: 已发布,六月完成二次闭环
初始修复版本: RELEASE.2026-04-17T00-00-00Z
完整修复版本: RELEASE.2026-06-18T00-00-00Z
GitHub Issue: pgsty/minio#25

四月的第一轮修复使用既有的 1 MiB maxCharsPerRecord 同时限制 CSV 与普通 JSON Lines,避免在遇到分隔符前持续无界 buffering,并让客户端得到明确的 OverMaxRecordSize。六月复核却发现,支持 SIMD 的 CPU 会走另一条 simdjson fast path,完全绕过这个限制。

最终方案让 JSON Lines 统一走 bounded reader,同时修正错误码、parser error 与 terminal error 前的 completed-record flush。代价是暂时放弃 SIMD 快路径,以换取所有 CPU 上一致的安全语义。

威胁模型

攻击者可以提交或查询包含超长单条记录的对象。reader 在遇到 record delimiter 前持续缓存,造成 memory/CPU DoS。更麻烦的是,同一个输入会因为机器 CPU 能力不同而进入不同实现:测试机上的安全行为,并不一定等于生产机。

错误语义也属于修复的一部分。如果超大记录最后只表现为 generic InternalError,客户端与告警系统无法区分安全上限和服务端故障。

第一轮:复用已有的 1 MiB 不变量

第一版补丁没有发明新的配置项,而是沿用已经存在的 maxCharsPerRecord = 1 MiB

  • CSV splitter 与 line-delimited JSON 在 buffer/parse 前拒绝超长记录;
  • 保留最早发生的 splitter error,不让 partial decode 覆盖;
  • 将错误透传为 OverMaxRecordSize,不再折叠成 InternalError

这是一项有意的兼容性收缩。过去包含超过 1 MiB 单行或单记录的客户端,升级后必须切分输入。

第二轮:硬件相关的绕过

六月沿调用链继续检查时发现:

JSON Lines -> simdj.NewReader -> simdjson.ParseNDStream

simdjson.SupportedCPU() 为真时,JSON Lines 绕过 bounded json.PReader。第三方 parser 会在 chunk 结束后继续读取直到换行,普通 reader wrapper 无法同时做到“不丢失前面完整记录”和“下一条记录一定有界”。

最终选择不是继续包裹,而是让 JSON Lines 暂时全部走 bounded PReader。未来如果恢复 SIMD,它必须自己执行同样的 record bound,并通过同一组不依赖 CPU 的回归测试。

同一轮修正的流语义

复核还修正了几个相邻问题:

  • errors.As 透传实现 SelectError 的错误,而不是只识别一个 concrete type;
  • JSON worker 把 parser error 包装成 JSONParsingError
  • terminal error event 之前先 flush 已完成但不足 batch size 的 output queue;
  • 保留输入顺序中的错误优先级,不用更晚的 oversized record 覆盖更早的 parse error。

这些细节决定了客户端看到的是正确的失败,而不是“修复了资源上限,却破坏了流式协议”。

刻意没有塞进本 CVE 的问题

  • CSV AllowQuotedRecordDelimiter 与外层物理换行 splitter 的历史语义缺陷;
  • CRLF 中 \r 是否计入长度;
  • 在没有相同边界的情况下恢复 SIMD 性能。

这些问题有的真实存在,但需要独立的 AWS compatibility evidence 或更复杂的 quote-aware splitter,不适合借安全修复顺手猜答案。

验证与发布

历史记录包含 oversized JSON Lines、错误码保留和不依赖本机 SIMD 能力的行为测试;go test ./internal/s3select/... -count=1git diff --check 均有通过记录。

公开初始修复提交为 c5765dc,六月完整修复为 fd69c89。本次博客整理没有重新执行这些测试。

最终代价

  • JSON Lines 性能可能下降,本事件没有 benchmark 给出量化结果;
  • 1 MiB 单记录上限会拒绝过去可接受的超大输入;
  • quoted CSV multiline 语义仍需独立处理;
  • 任何 CPU-specific fast path 以后都必须与 slow path 共用安全测试。

这次二次修复留下的教训是:安全不变量必须跨硬件路径成立。只在当前 CPU 上跑绿的测试,不能证明另一个执行引擎也受保护。

最后修改 August 2, 2026: init commit (8338d5b)