
Harnais d'audit testant si la classe de bug de récursion de déballage Any CVE-2026-0994 affecte le cœur C d'upb dans les bindings protobuf Ruby et PHP, avec des charges utiles ASan/UBSan.
Suite aux CVE de récursion de déballage Any confirmés dans les bindings Python
et JavaScript de protobuf (plus récemment CVE-2026-0994, un bug purement Python dans
_ConvertAnyMessage de json_format.py, qui récursait via methodcaller()
au lieu de ConvertMessage() et sautait silencieusement le compteur de profondeur).
Question : la même classe de bug existe-t-elle dans le cœur C upb intégré
dans les extensions natives Ruby et PHP ?
Réponse courte : non. Le chemin de décodage JSON et le chemin de décodage binaire wire appliquent tous deux correctement leurs limites de récursion dans tous les cas testés ici, y compris une charge de stress à 200 000 niveaux exécutée sous une pile de 1 Mo. Il s'agit d'une hypothèse falsifiée avec preuves, pas d'une vulnérabilité — elle est consignée ici comme un résultat négatif propre, de la même manière qu'on consignerait une hypothèse falsifiée dans n'importe quel autre fil de recherche.
Les extensions natives Ruby et PHP de protobuf intègrent chacune un amalgame
mono-fichier du cœur C de upb (ruby-upb.c, php-upb.c) plutôt que de lier
une bibliothèque partagée. Cet audit compile ce fichier exact comme cible C
autonome — sans runtime Ruby ni PHP impliqué — initialise un upb_DefPool avec
les descripteurs google.protobuf.Any / Struct / Value / ListValue, et
pilote les véritables points d'entrée du décodeur (upb_JsonDecode, upb_Decode)
directement avec des charges adversariales, sous ASan/UBSan.
| Chemin | Garde | Limite par défaut | Résultat |
|---|---|---|---|
Décodage JSON, jsondec_any (Ruby) | d->depth, vérifié dans jsondec_push | 64 | Falsifié — erreur propre sur une charge à 5000 niveaux, l'offset d'octet correspond exactement à ~64 niveaux |
Décodage binaire wire, upb_Decode (Ruby) | Decode_LimitDepth | 100 | Falsifié — kUpb_DecodeStatus_MaxDepthExceeded propre sur une charge limite à 60 niveaux et sur une charge de stress à 200 000 niveaux, à la fois sous une pile de 8 Mo et une pile de 1 Mo (approximant un thread Ruby non principal) |
| Binding PHP | mêmes fonctions de garde | mêmes | Falsifié par preuve d'identité, pas par une exécution séparée — voir ci-dessous |
Aucune violation ASan ou UBSan n'a été observée dans aucune exécution. Aucun crash, aucun blocage, aucun épuisement de pile.
php-upb.c et ruby-upb.c diffèrent d'environ 1 964 lignes au total, mais
jsondec_any, jsondec_push et les constantes de limite de profondeur wire sont
octet pour octet identiques entre les deux fichiers (verify_php_identical.sh le prouve,
ne le prenez pas pour argent comptant — exécutez-le). Comme le code de garde lui-même est
prouvablement le même, le résultat Ruby se transpose sans nécessiter de harnais
PHP redondant.
JsonParser /
CodedInputStream avec leur propre gestion de RecursionLimit). Genuinement
non testé ; ce dépôt ne le couvre pas encore.utf8_range.c intégrée../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
Nécessite : gcc, protoc (apt-get install protobuf-compiler), python3,
git. Compilé et vérifié avec gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 et libprotoc 3.21.12 — des versions différentes peuvent se comporter
différemment ; si vos résultats ne correspondent pas au tableau ci-dessus, c'est une donnée, pas
un bug dans votre configuration. Découvrez pourquoi avant de supposer qu'il s'agit d'une incompatibilité
d'outillage.
third_party/ (récupéré par fetch_source.sh) contient le code source protobuf
de Google sous licence Apache-2.0, épinglé au commit ci-dessus. Il n'est
jamais commité dans ce dépôt — fetch_source.sh est l'ancre de reproductibilité
au lieu d'intégrer l'arbre source de quelqu'un d'autre dans celui-ci.