审计工具,用于测试 CVE-2026-0994 Any 解包递归漏洞类是否影响 Ruby 和 PHP protobuf 绑定中 upb 的 C 核心,并带有 ASan/UBSan 载荷。
针对 protobuf 的 Python 和 JavaScript 绑定中已确认的 Any 解包递归 CVE 的后续研究(最近的是 CVE-2026-0994,这是 json_format.py 中 _ConvertAnyMessage 的一个纯 Python bug,它通过 methodcaller() 而非 ConvertMessage() 进行递归,并静默跳过了深度计数器)。
问题: 捆绑在 Ruby 和 PHP 原生扩展中的 C upb 核心是否存在同一类 bug?
简短回答:不存在。 在此测试的每种情况下,JSON 解码路径和二进制线格式解码路径都正确执行了各自的递归限制,包括在 1MB 栈下运行的 200,000 层压力载荷。这是一个带有证据的被证伪假设,而非漏洞——它作为一次干净的否定结果记录在此,就像你在任何其他研究线程中记录被证伪的假设一样。
Protobuf 的 Ruby 和 PHP 原生扩展各自内置了 upb C 核心的单文件合并版本(ruby-upb.c、php-upb.c),而非链接共享库。本次审计将该确切文件构建为独立的 C 目标——不涉及 Ruby 或 PHP 运行时——使用 google.protobuf.Any / Struct / Value / ListValue 描述符引导一个 upb_DefPool,并在 ASan/UBSan 下用对抗性载荷直接驱动真实的解码器入口点(upb_JsonDecode、upb_Decode)。
在任何运行中均未观察到 ASan 或 UBSan 违规。无崩溃、无挂起、无栈耗尽。
php-upb.c 和 ruby-upb.c 整体相差约 1,964 行,但 jsondec_any、jsondec_push 以及线格式深度限制常量在两个文件之间逐字节相同(verify_php_identical.sh 证明了这一点,不要盲目相信——运行它)。由于防护代码本身可证明是相同的,Ruby 的结果无需冗余的 PHP 专用测试框架即可迁移适用。
JsonParser / CodedInputStream 有自己的 RecursionLimit 处理)。确实未经测试;本仓库尚未覆盖它。utf8_range.c 验证。./fetch_source.sh # pins & clones protobuf @ ead3f0029facc43da13588132e9091bf9bd7a26f
./build.sh # compiles both harnesses w/ ASan+UBSan against the fetched source
./run_tests.sh # generates payloads, runs the full matrix, prints results
./verify_php_identical.sh # confirms the PHP claim above instead of asserting it
需要:gcc、protoc(apt-get install protobuf-compiler)、python3、git。针对 gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 和 libprotoc 3.21.12 构建并验证——不同版本可能表现不同;如果你的结果与上表不符,那是数据,而不是你环境中的 bug。在假设是工具链不匹配之前,先找出原因。
third_party/(由 fetch_source.sh 获取)包含 Google 的 Apache-2.0 许可的 protobuf 源代码,固定到上述提交。它从未提交到此仓库——fetch_source.sh 是可复现性锚点,而不是将别人的源代码树 vendor 到本仓库中。
| 路径 | 防护 | 默认限制 | 结果 |
|---|
JSON 解码,jsondec_any(Ruby) | d->depth,在 jsondec_push 中检查 | 64 | 已证伪——5000 层载荷下干净报错,字节偏移与约 64 层完全吻合 |
二进制线格式解码,upb_Decode(Ruby) | Decode_LimitDepth | 100 | 已证伪——在 60 层边界载荷和 200,000 层压力载荷下均干净返回 kUpb_DecodeStatus_MaxDepthExceeded,两者均在 8MB 栈和 1MB 栈(近似非主 Ruby 线程)下测试 |
| PHP 绑定 | 相同的防护函数 | 相同 | 通过同一性证明被证伪,而非单独运行——见下文 |