
Тестовый стенд для аудита, проверяющий, влияет ли класс ошибок рекурсии Any-unwrapping CVE-2026-0994 на ядро C upb в привязках protobuf для Ruby и PHP, с полезными нагрузками ASan/UBSan.
Продолжение подтверждённых CVE, связанных с рекурсией при разворачивании Any в привязках protobuf для Python и JavaScript (совсем недавно — CVE-2026-0994, чисто питоновский баг в _ConvertAnyMessage из json_format.py, который рекурсивно вызывался через methodcaller() вместо ConvertMessage() и молча пропускал счётчик глубины).
Вопрос: существует ли тот же класс багов в ядре upb на C, встроенном в нативные расширения для Ruby и PHP?
Короткий ответ: нет. И путь JSON-декодирования, и путь бинарного wire-декодирования корректно соблюдают свои лимиты рекурсии во всех протестированных здесь случаях, включая стрессовую нагрузку в 200 000 уровней, запущенную при стеке в 1 МБ. Это опровергнутая гипотеза с доказательствами, а не уязвимость — она зафиксирована здесь как чистый отрицательный результат, точно так же, как вы бы залогировали опровергнутую гипотезу в любой другой исследовательской ветке.
Нативные расширения protobuf для Ruby и PHP каждое встраивает однофайловую амальгамацию C-ядра upb (ruby-upb.c, php-upb.c), а не линкуется с разделяемой библиотекой. Этот аудит собирает именно этот файл как самостоятельную C-цель — без участия рантаймов Ruby или PHP — инициализирует upb_DefPool дескрипторами google.protobuf.Any / Struct / Value / ListValue и напрямую прогоняет реальные точки входа декодера (upb_JsonDecode, upb_Decode) состязательными нагрузками под ASan/UBSan.
| Путь | Защита | Лимит по умолчанию | Результат |
|---|---|---|---|
JSON-декодирование, jsondec_any (Ruby) | d->depth, проверяется в jsondec_push | 64 | Опровергнуто — чистая ошибка на нагрузке в 5000 уровней, смещение в байтах точно соответствует ~64 уровням |
Бинарное wire-декодирование, upb_Decode (Ruby) | Decode_LimitDepth | 100 | Опровергнуто — чистый kUpb_DecodeStatus_MaxDepthExceeded на граничной нагрузке в 60 уровней и на стрессовой нагрузке в 200 000 уровней, как при стеке 8 МБ, так и при стеке 1 МБ (имитация неосновного потока Ruby) |
| Привязка PHP | те же функции защиты | те же | Опровергнуто доказательством идентичности, а не отдельным прогоном — см. ниже |
Ни в одном прогоне не наблюдалось нарушений ASan или UBSan. Ни краха, ни зависания, ни исчерпания стека.
php-upb.c и ruby-upb.c в целом различаются примерно на 1 964 строки, но jsondec_any, jsondec_push и константы лимита глубины для wire побайтово идентичны в обоих файлах (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 — другие версии могут вести себя иначе; если ваши результаты не совпадают с таблицей выше, это данные, а не баг в вашей конфигурации. Разберитесь почему, прежде чем предполагать несоответствие инструментов.
third_party/ (загружается через fetch_source.sh) содержит исходники protobuf от Google под лицензией Apache-2.0, закреплённые на указанном выше коммите. Они никогда не коммитятся в этот репозиторий — fetch_source.sh служит якорем воспроизводимости вместо вендоринга чужого дерева исходников в это.