Data di pubblicazione: 19 marzo 2025
Skrifa è scritto in Rust ed è stato creato per sostituire FreeType al fine di rendere sicura l'elaborazione dei caratteri in Chrome per tutti i nostri utenti. Skifra sfrutta la sicurezza della memoria di Rust e ci consente di eseguire più rapidamente l'iterazione sui miglioramenti della tecnologia dei caratteri in Chrome. Il passaggio da FreeType a Skrifa ci consente di essere agili e sicuri quando apportiamo modifiche al codice dei caratteri. Ora impieghiamo molto meno tempo per correggere i bug di sicurezza, con conseguenti aggiornamenti più rapidi e una qualità del codice migliore.
Questo post spiega perché Chrome ha abbandonato FreeType e alcuni dettagli tecnici interessanti sui miglioramenti resi possibili da questo passaggio.
Perché sostituire FreeType?
Il web è unico in quanto consente agli utenti di recuperare risorse non attendibili da una vasta gamma di fonti non attendibili con l'aspettativa che tutto funzioni e che sia sicuro farlo. Questa assunzione è generalmente corretta, ma mantenere questa promessa agli utenti ha un costo. Ad esempio, per utilizzare in sicurezza un carattere web (un carattere caricato sulla rete), Chrome utilizza diverse misure di mitigazione della sicurezza:
- L'elaborazione dei caratteri è in sandbox in base alla regola di due: non sono attendibili e il codice di utilizzo non è sicuro.
- I caratteri vengono passati attraverso lo strumento di pulizia OpenType prima dell'elaborazione.
- Tutte le librerie coinvolte nella decompressione e nell'elaborazione dei caratteri sono sottoposte a test di fuzz.
Chrome viene fornito con FreeType e lo utilizza come libreria di elaborazione dei caratteri principale su Android, ChromeOS e Linux. Ciò significa che molti utenti sono esposti se esiste una vulnerabilità in FreeType.
La libreria FreeType viene utilizzata da Chrome per calcolare le metriche e caricare gli schemi suggeriti dai caratteri. Nel complesso, l'utilizzo di FreeType è stato un grande vantaggio per Google. Svolge un compito complesso e lo fa bene, ci basiamo molto su di essa e diamo il nostro contributo. Tuttavia, è scritto in codice non sicuro e ha avuto origine in un periodo in cui gli input dannosi erano meno probabili. Il solo tenere il passo con lo stream di problemi rilevati tramite il fuzzing costa a Google almeno 0,25 ingegneri software a tempo pieno. Inoltre, notiamo che non troviamo tutto o che troviamo elementi solo dopo che il codice è stato inviato agli utenti.
Questo modello di problemi non è esclusivo di FreeType, abbiamo notato che anche altre librerie non sicure presentano problemi anche quando utilizziamo i migliori ingegneri del software che possiamo trovare, sottoponiamo a revisione del codice ogni modifica e richiediamo test.
Perché i problemi continuano a verificarsi
Durante la valutazione della sicurezza di FreeType, abbiamo osservato tre classi principali di problemi (elenco non esaustivo):
Utilizzo di un linguaggio non sicuro
Pattern/Problema | Esempio |
---|---|
Gestione manuale della memoria |
|
Accesso all'array non controllato | CVE-2022-27404 |
Sforzi di numeri interi | Durante l'esecuzione di macchine virtuali incorporate per l'ottimizzazione TrueType del disegno e dell'ottimizzazione CFF https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
Utilizzo errato dell'allocazione con azzeramento rispetto a quella senza azzeramento | Discussione in https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94, 8 problemi di fuzzer trovati in seguito |
Trasmissione non valida | Consulta la riga seguente sull'utilizzo delle macro |
Problemi specifici del progetto
Pattern/Problema | Esempio |
---|---|
Le macro nascondono la mancanza di specificazione esplicita delle dimensioni |
|
Il nuovo codice aggiunge costantemente bug, anche se scritto in modo sicuro. |
|
Mancanza di test |
|
Problemi di dipendenza
Il fuzzing ha identificato ripetutamente problemi nelle librerie su cui si basa FreeType, come bzip2, libpng e zlib. Ad esempio, confronta freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate.
Il fuzzing non è sufficiente
Il fuzzing, ovvero i test automatici con una vasta gamma di input, inclusi quelli non validi randomizzati, ha lo scopo di trovare molti dei tipi di problemi che si verificano nella release stabile di Chrome. Eseguiamo fuzz su FreeType nell'ambito del progetto oss-fuzz di Google. Sebbene riesca a trovare problemi, i caratteri si sono dimostrati piuttosto resistenti al fuzzing per i seguenti motivi.
I file dei caratteri sono complessi, paragonabili ai file video in quanto contengono più tipi di informazioni diverse. I file dei caratteri sono un formato contenitore per più tabelle, in cui ogni tabella ha uno scopo diverso nell'elaborazione di testo e caratteri per produrre un glifo posizionato correttamente sullo schermo. In un file del carattere troverai:
- Metadati statici come nomi e parametri dei caratteri per i caratteri variabili.
- Mappature dai caratteri Unicode ai glifi.
- Regole e grammatica complesse per il layout dello schermo dei glifi.
- Informazioni visive: forme dei glifari e informazioni sulle immagini che descrivono l'aspetto dei glifari posizionati sullo schermo.
- Le tabelle visive possono a loro volta includere programmi di indicazione TrueType, che sono mini programmi eseguiti per modificare la forma dei glifi.
- Stringhe di caratteri nelle tabelle CFF o CFF2 che sono istruzioni imperative per il disegno e gli indicatori delle curve eseguite nel motore di rendering CFF.
I file dei caratteri sono complessi in quanto hanno un proprio linguaggio di programmazione e un'elaborazione della macchina a stati, che richiedono macchine virtuali specifiche per essere eseguiti.
A causa della complessità del formato, il fuzzing presenta delle carenze nel rilevare i problemi nei file dei caratteri.
È difficile ottenere una buona copertura del codice o un buon avanzamento del fuzzer per i seguenti motivi:
- I programmi di fuzzing degli indicatori TrueType, le stringhe di caratteri CFF e il layout OpenType che utilizzano semplici mutatori di tipo bit-flipping/shift/insertion/deletion hanno difficoltà a raggiungere tutte le combinazioni di stati.
- Il fuzzing deve produrre almeno strutture parzialmente valide. La mutazione random raramente lo fa, rendendo difficile ottenere una buona copertura, in particolare per i livelli di codice più profondi.
- I tentativi di fuzzing attuali in ClusterFuzz e oss-fuzz non utilizzano ancora la mutazione consapevole della struttura. L'utilizzo di mutatori attenti alla grammatica o alla struttura potrebbe contribuire a evitare la produzione di varianti che vengono rifiutate in anticipo, a costo di un tempo di sviluppo più lungo e dell'introduzione di probabilità che manchino parti dello spazio di ricerca.
Affinché il fuzzing possa progredire, i dati in più tabelle devono essere sincronizzati:
- I pattern di mutazione usuali dei fuzzer non producono dati parzialmente validi, pertanto molte iterazioni vengono rifiutate e l'avanzamento diventa lento.
- La mappatura dei glifi, le tabelle di layout OpenType e il disegno dei glifi sono collegati e dipendono l'uno dall'altro, formando uno spazio combinatorio i cui angoli sono difficili da raggiungere con il fuzzing.
- Ad esempio, sono stati necessari più di 10 mesi per trovare la vulnerabilità tt_face_get_paint COLRv1 di gravità elevata.
Nonostante il nostro impegno, i problemi di sicurezza dei caratteri hanno raggiunto ripetutamente gli utenti finali. La sostituzione di FreeType con un'alternativa Rust eviterà più intere classi di vulnerabilità.
Skrifa in Chrome
Skia è la libreria grafica utilizzata da Chrome. Skia si basa su FreeType per caricare i metadati e le lettere dai caratteri. Skrifa è una libreria Rust, che fa parte della famiglia di librerie Fontations, che fornisce una sostituzione sicura per le parti di FreeType utilizzate da Skia.
Per eseguire la transizione da FreeType a Skia, il team di Chrome ha sviluppato un nuovo backend per i caratteri Skia basato su Skrifa e ha implementato gradualmente la modifica per gli utenti:
- In Chrome 128 (agosto 2024) abbiamo attivato Fontations per l'utilizzo in formati di caratteri meno comuni, ad esempio per i caratteri a colori e CFF2, come prova sicura.
- In Chrome 133 (febbraio 2025) abbiamo attivato Fontations per tutto l'utilizzo dei caratteri web su Linux, Android e ChromeOS e per l'utilizzo dei caratteri web come alternativa su Windows e Mac, nei casi in cui il sistema non supporti un formato di carattere, ma Chrome debba visualizzarlo.
Per l'integrazione in Chrome, ci basiamo sull'integrazione fluida di Rust nel codebase introdotta dal team di sicurezza di Chrome.
In futuro, passeremo a Fontations anche per i caratteri dei sistemi operativi, iniziando da Linux e ChromeOS, poi su Android.
Sicurezza, prima di tutto
Il nostro obiettivo principale è ridurre (o idealmente eliminare) le vulnerabilità di sicurezza causate da accessi non consentiti alla memoria. Rust lo fornisce out-of-the-box, a condizione che tu eviti blocchi di codice non sicuri.
I nostri obiettivi di rendimento ci richiedono di eseguire un'operazione attualmente non sicura: la reinterpretazione di byte arbitrari come struttura di dati fortemente tipizzata. In questo modo possiamo leggere i dati da un file del carattere senza eseguire copie non necessarie ed è essenziale per produrre un parser dei caratteri veloce.
Per evitare di scrivere codice non sicuro, abbiamo scelto di esternalizzare questa responsabilità a bytemuck, una libreria Rust progettata appositamente per questo scopo, ampiamente testata e utilizzata nell'ecosistema. La concentrazione della reinterpretazione dei dati non elaborati in bytemuck ci consente di avere questa funzionalità in un unico posto e di sottoporla a controllo, nonché di evitare di ripetere codice non sicuro per lo scopo. Il progetto transmutazione sicura mira a incorporare questa funzionalità direttamente nel compilatore Rust e eseguiremo il passaggio non appena sarà disponibile.
La correttezza è importante
Skrifa è costituito da componenti indipendenti in cui la maggior parte delle strutture di dati è progettata per essere immutabile. Ciò migliora la leggibilità, la manutenibilità e il multithreading. Inoltre, rende il codice più adatto ai test di unità. Abbiamo sfruttato questa opportunità e abbiamo prodotto una suite di circa 700 test di unità che coprono l'intero stack, dalle routine di analisi a basso livello alle macchine virtuali con suggerimenti di alto livello.
La correttezza implica anche fedeltà e FreeType è molto apprezzato per la generazione di contorni di alta qualità. Dobbiamo eguagliare questa qualità per essere una sostituzione appropriata. A tal fine, abbiamo creato uno strumento personalizzato chiamato fauntlet che confronta l'output di Skrifa e FreeType per batch di file di caratteri in una ampia gamma di configurazioni. Questo ci offre una certa certezza di poter evitare regressioni della qualità.
Inoltre, prima dell'integrazione in Chromium, abbiamo eseguito un ampio insieme di confronti dei pixel in Skia, confrontando il rendering di FreeType con quello di Skrifa e Skia per garantire che le differenze di pixel siano assolutamente minime in tutte le modalità di rendering richieste (in diverse modalità di antialiasing e di indicazione).
I test di fuzz sono uno strumento importante per determinare in che modo un software reagirà a input non validi e dannosi. Stiamo sottoponendo a fuzzing continuo il nostro nuovo codice da giugno 2024. Sono coperte le librerie Rust stesse e il codice di integrazione. Sebbene il fuzzer abbia trovato (al momento della stesura di questo articolo) 39 bug, è importante notare che nessuno di questi è stato considerato di fondamentale importanza per la sicurezza. Possono causare risultati visivi indesiderati o persino arresti anomali controllati, ma non generano vulnerabilità sfruttabili.
Avanti!
Siamo molto soddisfatti dei risultati dei nostri sforzi per utilizzare Rust per il testo. Offrire agli utenti un codice più sicuro e aumentare la produttività degli sviluppatori è un grande traguardo per noi. Abbiamo intenzione di continuare a cercare opportunità per utilizzare Rust nelle nostre piattaforme di text. Se vuoi saperne di più, Oxidize illustra alcuni dei piani futuri di Google Fonts.