
Harness di audit che verifica se la classe di bug di ricorsione Any-unwrapping CVE-2026-0994 influisce sul core C di upb nei binding protobuf di Ruby e PHP, con payload ASan/UBSan.
Stato: Completato. Sei controlli di protezione dalla ricorsione su tre implementazioni indipendenti (core upb condiviso tra Ruby/PHP, implementazione gestita separata di C#), tutti falsificati tranne la CVE Python già pubblica e già corretta rispetto alla quale questo audit è stato condotto. Non sono state trovate nuove vulnerabilità.
Seguito delle CVE confermate di ricorsione da scartamento di Any nei binding Python e JavaScript di protobuf (più recentemente CVE-2026-0994, un bug puramente Python in _ConvertAnyMessage di json_format.py, che ricorreva tramite methodcaller() invece di ConvertMessage() e saltava silenziosamente il contatore di profondità).
Domanda: la stessa classe di bug esiste nel core C upb incluso nelle estensioni native Ruby e PHP?
Risposta breve: no. Il percorso di decodifica JSON, il percorso di decodifica binaria dal wire e l'implementazione gestita indipendente in C# applicano tutti correttamente i propri limiti di ricorsione in ogni caso testato qui, incluso un payload di stress a 200.000 livelli eseguito con uno stack da 1MB. Questa è un'ipotesi falsificata con prove, non una vulnerabilità — è registrata qui come risultato negativo pulito, allo stesso modo in cui si registrerebbe un'ipotesi falsificata in qualsiasi altro filone di ricerca.
Le estensioni native Ruby e PHP di protobuf includono ciascuna un'amalgamazione in singolo file del core C di upb (ruby-upb.c, php-upb.c) invece di collegare una libreria condivisa. Questo audit compila quel file esatto come target C autonomo — senza alcun runtime Ruby o PHP coinvolto — inizializza un upb_DefPool con i descrittori di google.protobuf.Any / Struct / Value / ListValue, e pilota direttamente i reali punti di ingresso del decoder (upb_JsonDecode, upb_Decode) con payload avversariali, sotto ASan/UBSan.
| Percorso | Protezione | Limite predefinito | Risultato |
|---|---|---|---|
Decodifica JSON, jsondec_any (Ruby) | d->depth, verificato in jsondec_push | 64 | Falsificato — errore pulito con payload a 5000 livelli, l'offset in byte corrisponde esattamente a ~64 livelli |
Decodifica binaria dal wire, upb_Decode (Ruby) | Decode_LimitDepth | 100 | Falsificato — kUpb_DecodeStatus_MaxDepthExceeded pulito al payload di confine a 60 livelli e al payload di stress a 200.000 livelli, entrambi con uno stack da 8MB e uno stack da 1MB (approssimando un thread Ruby non principale) |
| Binding PHP | stesse funzioni di protezione | stesso | Falsificato per prova di identità, non con un'esecuzione separata — vedi sotto |
Decodifica JSON, JsonParser.MergeAny (C#, Google.Protobuf 3.36.1) | tokenizer.RecursionDepth, verificato in Merge() | 100 | Falsificato — confermato con payload a 200 livelli; MergeAny costruisce un nuovo JsonTokenizer tramite FromReplayedTokens a ogni scartamento di Any, ma la contabilità della profondità si propaga correttamente attraverso di esso |
Decodifica binaria dal wire, CodedInputStream/Value.Parser (C#, Google.Protobuf 3.36.1) | recursionDepth/recursionLimit sullo stream | 64 | Falsificato — InvalidProtocolBufferException pulita al payload di confine a 60 livelli (stessi byte di payload del test wire Ruby, riutilizzati direttamente) |
Nessuna violazione ASan o UBSan è stata osservata in alcuna esecuzione. Nessun crash, nessun blocco, nessun esaurimento dello stack.
php-upb.c e ruby-upb.c differiscono complessivamente di ~1.964 righe, ma jsondec_any, jsondec_push e le costanti del limite di profondità del wire sono identiche byte per byte tra i due file (verify_php_identical.sh lo dimostra, non fidatevi sulla parola — eseguitelo). Poiché il codice di protezione stesso è dimostrabilmente lo stesso, il risultato Ruby si trasferisce senza bisogno di un harness ridondante specifico per PHP.
Quattro binding testati (Ruby, PHP, C#, più la CVE già pubblica di Python), tutti falsificati tranne quella già corretta upstream. Filoni aperti rimanenti, nessuno di questa specifica classe di bug:
utf8_range.c incluso.dotnet run -- wire <payload>) non è ancora collegato alla CI — solo il test C# in modalità JSON è automatizzato../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
Richiede: gcc, protoc (apt-get install protobuf-compiler), python3,
git. Compilato e verificato con gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 e libprotoc 3.21.12 — versioni diverse possono comportarsi
diversamente; se i tuoi risultati non corrispondono alla tabella sopra, sono dati, non
un bug nella tua configurazione. Scopri il perché prima di presumere che si tratti di una discrepanza negli strumenti.
third_party/ (recuperato da fetch_source.sh) contiene il codice sorgente
protobuf di Google con licenza Apache-2.0, fissato al commit sopra indicato. Non viene
mai committato in questo repository — fetch_source.sh è l'ancora di riproducibilità
anziché includere l'albero dei sorgenti di qualcun altro in questo.