
Audit-Harness, das testet, ob die CVE-2026-0994-Rekursionsfehlerklasse beim Any-Unwrapping den C-Kern von upb in Ruby- und PHP-Protobuf-Bindings betrifft, mit ASan/UBSan-Payloads.
Status: Abgeschlossen. Sechs Rekursionsschutz-Prüfungen über drei unabhängige Implementierungen hinweg (gemeinsamer Ruby/PHP-upb-Kern, die separate verwaltete Implementierung von C#), alle widerlegt außer der bereits öffentlichen, bereits gepatchten Python-CVE, gegen die dieses Audit prüfte. Es wurden keine neuen Schwachstellen gefunden.
Nachfolgeuntersuchung zu bestätigten Rekursions-CVEs beim Entpacken von Any in den Python-
und JavaScript-Bindings von protobuf (zuletzt CVE-2026-0994, ein reiner Python-Fehler in
json_format.pys _ConvertAnyMessage, der über methodcaller() statt über ConvertMessage()
rekursierte und den Tiefenzähler stillschweigend übersprang).
Frage: Existiert dieselbe Fehlerklasse im C-Kern upb, der in die nativen Erweiterungen
für Ruby und PHP eingebettet ist?
Kurze Antwort: nein. Der JSON-Dekodierungspfad, der binäre Wire-Dekodierungspfad und die unabhängige verwaltete C#-Implementierung erzwingen ihre Rekursionsgrenzen in jedem hier getesteten Fall korrekt, einschließlich einer Stress-Nutzlast mit 200.000 Ebenen, die unter einem 1-MB-Stack ausgeführt wurde. Dies ist eine widerlegte Hypothese mit Belegen, keine Schwachstelle — sie wird hier als sauberes negatives Ergebnis festgehalten, so wie man eine widerlegte Hypothese in jedem anderen Forschungsstrang protokollieren würde.
Die nativen Erweiterungen von protobuf für Ruby und PHP bündeln jeweils eine Einzeldatei-
Amalgamierung des C-Kerns von upb (ruby-upb.c, php-upb.c), anstatt eine
gemeinsame Bibliothek zu linken. Dieses Audit baut genau diese Datei als eigenständiges C-
Ziel — ohne Ruby- oder PHP-Laufzeit — initialisiert einen upb_DefPool mit
google.protobuf.Any-/Struct-/Value-/ListValue-Deskriptoren und treibt die echten
Dekoder-Einstiegspunkte (upb_JsonDecode, upb_Decode) direkt mit adversarialen Nutzlasten
unter ASan/UBSan an.
| Pfad | Schutz | Standardgrenze | Ergebnis |
|---|---|---|---|
JSON-Dekodierung, jsondec_any (Ruby) | d->depth, geprüft in jsondec_push | 64 | Widerlegt — sauberer Fehler bei Nutzlast mit 5000 Ebenen, Byte-Offset entspricht exakt ~64 Ebenen |
Binäre Wire-Dekodierung, upb_Decode (Ruby) | Decode_LimitDepth | 100 | Widerlegt — sauberes kUpb_DecodeStatus_MaxDepthExceeded bei Grenznutzlast mit 60 Ebenen und bei Stress-Nutzlast mit 200.000 Ebenen, beide unter einem 8-MB-Stack und einem 1-MB-Stack (angenähert an einen Nicht-Main-Ruby-Thread) |
| PHP-Binding | dieselben Schutzfunktionen | dieselben | Widerlegt durch Identitätsnachweis, nicht durch einen separaten Lauf — siehe unten |
JSON-Dekodierung, JsonParser.MergeAny (C#, Google.Protobuf 3.36.1) | tokenizer.RecursionDepth, geprüft in Merge() | 100 | Widerlegt — bestätigt bei Nutzlast mit 200 Ebenen; MergeAny konstruiert pro Any-Entpackung einen neuen JsonTokenizer über FromReplayedTokens, aber die Tiefenzählung wird korrekt durch ihn hindurch fortgeführt |
Binäre Wire-Dekodierung, CodedInputStream/Value.Parser (C#, Google.Protobuf 3.36.1) | recursionDepth/recursionLimit am Stream | 64 | Widerlegt — saubere InvalidProtocolBufferException bei Grenznutzlast mit 60 Ebenen (dieselben Nutzlast-Bytes wie beim Ruby-Wire-Test, direkt wiederverwendet) |
In keinem Lauf wurde eine ASan- oder UBSan-Verletzung beobachtet. Kein Absturz, kein Hänger, keine Stack-Erschöpfung.
php-upb.c und ruby-upb.c unterscheiden sich insgesamt um ~1.964 Zeilen, aber
jsondec_any, jsondec_push und die Tiefengrenzen-Konstanten für Wire sind
byte-identisch zwischen den beiden Dateien (verify_php_identical.sh beweist
dies, verlassen Sie sich nicht darauf — führen Sie es aus). Da der Schutzcode selbst
nachweislich derselbe ist, überträgt sich das Ruby-Ergebnis, ohne dass ein redundanter
PHP-spezifischer Harness nötig wäre.
Vier Bindings getestet (Ruby, PHP, C#, plus die bereits öffentliche CVE von Python), alle widerlegt außer derjenigen, die bereits upstream gepatcht wurde. Verbleibende offene Stränge, keiner davon diese spezifische Fehlerklasse:
utf8_range.c.dotnet run -- wire <payload>) ist noch nicht in
CI eingebunden — nur der C#-Test im JSON-Modus ist automatisiert../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
Erfordert: gcc, protoc (apt-get install protobuf-compiler), python3,
git. Gebaut und verifiziert mit gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 und libprotoc 3.21.12 — andere Versionen können sich
anders verhalten; wenn deine Ergebnisse nicht mit der obigen Tabelle übereinstimmen, sind das Daten, kein
Fehler in deinem Setup. Finde heraus, warum, bevor du annimmst, dass es eine Tooling-Diskrepanz ist.
third_party/ (abgerufen durch fetch_source.sh) enthält Googles
Apache-2.0-lizenzierten protobuf-Quellcode, gepinnt auf den obigen Commit. Er wird
niemals in dieses Repo committet — fetch_source.sh ist der Reproduzierbarkeits-
Anker, anstatt den Quellbaum eines anderen in diesen zu vendorn.