CVE-2026-39414:S3 Select 超大记录与 SIMD 绕过
状态: 已发布,六月完成二次闭环
初始修复版本: 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=1 与 git diff --check 均有通过记录。
公开初始修复提交为 c5765dc,六月完整修复为 fd69c89。本次博客整理没有重新执行这些测试。
最终代价
- JSON Lines 性能可能下降,本事件没有 benchmark 给出量化结果;
- 1 MiB 单记录上限会拒绝过去可接受的超大输入;
- quoted CSV multiline 语义仍需独立处理;
- 任何 CPU-specific fast path 以后都必须与 slow path 共用安全测试。
这次二次修复留下的教训是:安全不变量必须跨硬件路径成立。只在当前 CPU 上跑绿的测试,不能证明另一个执行引擎也受保护。