Geheugenveiligheid voor weblettertypen

Dominik Rottsches
Dominik Röttsches
Rod Sheeter
Rod Sheeter
Tsjaad Brokaw
Chad Brokaw

Gepubliceerd: 19 maart 2025

Skrifa is geschreven in Rust en gemaakt als vervanging voor FreeType om de verwerking van lettertypen in Chrome veilig te maken voor al onze gebruikers. Skifra profiteert van de geheugenveiligheid van Rust en laat ons sneller verbeteringen in de lettertypetechnologie in Chrome doorvoeren. Door van FreeType naar Skrifa te gaan, kunnen we zowel wendbaar als onbevreesd zijn bij het aanbrengen van wijzigingen in onze lettertypecode. We besteden nu veel minder tijd aan het oplossen van beveiligingsfouten, wat resulteert in snellere updates en een betere codekwaliteit.

Dit bericht deelt waarom Chrome is afgestapt van FreeType, en enkele interessante technische details van de verbeteringen die deze stap mogelijk heeft gemaakt.

Waarom FreeType vervangen?

Het web is uniek omdat het gebruikers in staat stelt niet-vertrouwde bronnen op te halen uit een grote verscheidenheid aan niet-vertrouwde bronnen, in de verwachting dat alles gewoon zal werken, en dat ze dat veilig kunnen doen. Deze veronderstelling is over het algemeen juist, maar het nakomen van die belofte aan gebruikers brengt kosten met zich mee. Om bijvoorbeeld een weblettertype (een lettertype dat via het netwerk wordt geleverd) veilig te gebruiken, maakt Chrome gebruik van verschillende beveiligingsmaatregelen:

  • De verwerking van lettertypen gebeurt in een sandbox volgens de regel van twee : ze zijn onbetrouwbaar en de code die ze gebruiken is onveilig.
  • Lettertypen worden vóór verwerking door de OpenType Sanitizer gehaald.
  • Alle bibliotheken die betrokken zijn bij het decomprimeren en verwerken van lettertypen zijn op fuzz getest .

Chrome wordt geleverd met FreeType en gebruikt het als de primaire bibliotheek voor lettertypeverwerking op Android, ChromeOS en Linux. Dat betekent dat veel gebruikers worden blootgesteld als er een kwetsbaarheid in FreeType zit.

De FreeType-bibliotheek wordt door Chrome gebruikt om statistieken te berekenen en hints van lettertypen te laden. Over het geheel genomen is het gebruik van FreeType een enorme overwinning voor Google geweest. Het doet een complexe taak, en doet het goed. We vertrouwen er uitgebreid op en dragen er ook aan bij. Het is echter geschreven in onveilige code en vindt zijn oorsprong in een tijd waarin kwaadaardige invoer minder waarschijnlijk was. Alleen al het bijhouden van de stroom van problemen die door fuzzing worden gevonden, kost Google minstens 0,25 fulltime software-ingenieurs. Erger nog, we vinden waarneembaar niet alles of vinden dingen pas nadat de code naar gebruikers is verzonden.

Dit patroon van problemen is niet uniek voor FreeType; we zien dat andere onveilige bibliotheken problemen toegeven, zelfs als we de beste software-ingenieurs gebruiken die we kunnen vinden, de code elke wijziging beoordelen en tests vereisen.

Waarom problemen blijven binnensluipen

Toen we de beveiliging van FreeType evalueerden, constateerden we dat er drie hoofdcategorieën van problemen optraden (niet-uitputtend):

Gebruik van een onveilige taal

Patroon/probleem Voorbeeld
Handmatig geheugenbeheer
Niet-aangevinkte array-toegang CVE-2022-27404
Overloop van gehele getallen Tijdens de uitvoering van ingebedde virtuele machines voor TrueType-hints van CFF-tekenen en hints
https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow
Onjuist gebruik van toewijzing op nul versus niet-nul Discussie in https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94 , achteraf 8 fuzzer-problemen gevonden
Ongeldige casts Zie de volgende rij over macrogebruik

Projectspecifieke problemen

Patroon/probleem Voorbeeld
Macro's verdoezelen het ontbreken van expliciete grootte-invoer
  • Macro's zoals FT_READ_* en FT_PEEK_* verdoezelen welke typen gehele getallen worden gebruikt, waardoor wordt verborgen dat C99-typen met expliciete groottes (int16_t, enz.) niet worden gebruikt
Nieuwe code voegt consequent bugs toe, zelfs als deze defensief is geschreven.
  • COLRv1 en OT-SVG ondersteunen beide geproduceerde problemen
  • Fuzzing vindt enkele, maar niet noodzakelijkerwijs alle, #32421 , #52404
Gebrek aan tests
  • Het maken van testlettertypen is tijdrovend en moeilijk

Afhankelijkheidsproblemen

Fuzzing heeft herhaaldelijk problemen geïdentificeerd in bibliotheken waarvan FreeType afhankelijk is, zoals bzip2, libpng en zlib. Vergelijk bijvoorbeeld freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate .

Fuzzen is niet genoeg

Fuzzing (geautomatiseerd testen met een breed scala aan invoer, inclusief gerandomiseerde ongeldige invoer) is bedoeld om veel van de soorten problemen te vinden die in de stabiele release van Chrome voorkomen. We fuzzen FreeType als onderdeel van het oss-fuzz -project van Google. Er worden wel problemen gevonden, maar lettertypen zijn enigszins resistent gebleken tegen fuzzing, om de volgende redenen.

Lettertypebestanden zijn complex en vergelijkbaar met videobestanden, omdat ze meerdere soorten informatie bevatten. Lettertypebestanden zijn een containerformaat voor meerdere tabellen, waarbij elke tabel een ander doel dient bij het samen verwerken van tekst en lettertypen om een ​​correct gepositioneerde glyph op het scherm te produceren. In een lettertypebestand vindt u:

  • Statische metadata zoals lettertypenamen en parameters voor variabele lettertypen.
  • Toewijzingen van Unicode-tekens naar glyphs.
  • Een complexe regelset en grammatica voor de schermindeling van glyphs.
  • Visuele informatie: Glyph-vormen en afbeeldingsinformatie die beschrijven hoe de op het scherm geplaatste glyphs eruitzien.
    • De visuele tabellen kunnen op hun beurt TrueType-hintprogramma's bevatten, dit zijn miniprogramma's die worden uitgevoerd om de glyph-vorm te veranderen.
    • Tekenreeksen in de CFF- of CFF2-tabellen die dwingende instructies voor het tekenen van curven en hints zijn die worden uitgevoerd in de CFF-renderingengine.

Er is een complexiteit in lettertypebestanden die gelijk staat aan het hebben van een eigen programmeertaal en verwerking van statusmachines, waarvoor specifieke virtuele machines nodig zijn om ze uit te voeren.

Vanwege de complexiteit van het formaat heeft fuzzing tekortkomingen bij het vinden van problemen in lettertypebestanden.

Goede codedekking of fuzzer-voortgang is om de volgende redenen moeilijk te bereiken:

  • Fuzzing TrueType-hintprogramma's, CFF-tekenreeksen en OpenType-indeling met behulp van eenvoudige bit-flipping/shift/insertion/deletion-stijl mutators hebben moeite om alle combinaties van toestanden te bereiken.
  • Fuzzing moet op zijn minst gedeeltelijk geldige structuren opleveren. Willekeurige mutatie doet dit zelden, waardoor een goede dekking moeilijk te bereiken is, vooral voor diepere codeniveaus.
  • De huidige fuzzing-inspanningen in ClusterFuzz en oss-fuzz maken nog geen gebruik van structuurbewuste mutatie. Het gebruik van grammatica- of structuurbewuste mutators kan de productie van varianten helpen voorkomen die vroegtijdig worden afgewezen, ten koste van meer tijd om zich te ontwikkelen, en het introduceren van kansen die delen van de zoekruimte missen.

Gegevens in meerdere tabellen moeten gesynchroniseerd zijn zodat fuzzing vooruitgang kan boeken:

  • De gebruikelijke mutatiepatronen van fuzzers produceren geen gedeeltelijk geldige gegevens, waardoor veel iteraties worden afgewezen en de voortgang traag wordt.
  • De glyph-toewijzing, de OpenType-opmaaktabellen en de glyph-tekening zijn met elkaar verbonden en afhankelijk van elkaar, waardoor een combinatorische ruimte ontstaat waarvan de hoeken moeilijk te bereiken zijn met fuzzing.
  • Het kostte bijvoorbeeld meer dan tien maanden om de zeer ernstige kwetsbaarheid tt_face_get_paint COLRv1 te vinden.

Ondanks onze inspanningen hebben problemen met de lettertypebeveiliging herhaaldelijk eindgebruikers bereikt. Het vervangen van FreeType door een Rust-alternatief voorkomt meerdere hele klassen van kwetsbaarheden.

Skrifa in Chrome

Skia is de grafische bibliotheek die door Chrome wordt gebruikt. Skia vertrouwt op FreeType om metagegevens en lettervormen uit lettertypen te laden. Skrifa is een Rust-bibliotheek, onderdeel van de Fontations -bibliothekenfamilie, die een veilige vervanging biedt voor de delen van FreeType die door Skia worden gebruikt.

Om FreeType over te zetten naar Skia heeft het Chrome-team een ​​nieuwe Skia-lettertype-backend ontwikkeld op basis van Skrifa en deze wijziging geleidelijk aan voor gebruikers uitgerold:

Voor de integratie in Chrome vertrouwen we op de soepele integratie van Rust in de codebase geïntroduceerd door het Chrome-beveiligingsteam .

In de toekomst zullen we ook voor lettertypen van het besturingssysteem overstappen op lettertypen, te beginnen met Linux en ChromeOS, en vervolgens op Android.

Veiligheid voorop

Ons primaire doel is het verminderen (of idealiter elimineren!) van beveiligingsproblemen die worden veroorzaakt door toegang tot geheugen buiten de grenzen. Rust biedt dit kant-en-klaar, zolang u onveilige codeblokken vermijdt.

Onze prestatiedoelstellingen vereisen dat we één operatie uitvoeren die momenteel onveilig is: herinterpretatie van willekeurige bytes als een sterk getypeerde datastructuur. Hierdoor kunnen we de gegevens uit een lettertypebestand lezen zonder onnodige kopieën uit te voeren, en dit is essentieel voor het produceren van een snelle lettertypeparser.

Om onze eigen onveilige code te vermijden, hebben we ervoor gekozen deze verantwoordelijkheid uit te besteden aan bytemuck , een Rust-bibliotheek die speciaal voor dit doel is ontworpen en die breed wordt getest en gebruikt in het hele ecosysteem. Het concentreren van de herinterpretatie van ruwe gegevens in bytemuck zorgt ervoor dat we deze functionaliteit op één plek hebben en worden gecontroleerd, en voorkomen dat onveilige code voor dit doel wordt herhaald. Het veilige transmute-project heeft tot doel deze functionaliteit rechtstreeks in de Rust-compiler op te nemen en we zullen de overstap maken zodra deze beschikbaar is.

Correctheid is belangrijk

Skrifa is opgebouwd uit onafhankelijke componenten waarbij de meeste datastructuren zijn ontworpen om onveranderlijk te zijn. Dit verbetert de leesbaarheid, onderhoudbaarheid en multithreading. Het maakt de code ook beter geschikt voor unit-testen. We hebben van deze mogelijkheid gebruik gemaakt en een reeks van grofweg 700 unit-tests geproduceerd die onze volledige stapel bestrijken, van parseerroutines op laag niveau tot virtuele machines met hints op hoog niveau.

Correctheid impliceert ook trouw en FreeType staat hoog aangeschreven vanwege het genereren van hoogwaardige contouren. Deze kwaliteit moeten wij evenaren om een ​​geschikte vervanger te zijn. Daartoe hebben we een op maat gemaakte tool gebouwd, genaamd fauntlet , die de uitvoer van Skrifa en FreeType vergelijkt voor batches lettertypebestanden in een breed scala aan configuraties. Dit geeft ons enige zekerheid dat we achteruitgang in kwaliteit kunnen voorkomen.

Bovendien hebben we vóór de integratie in Chromium een ​​groot aantal pixelvergelijkingen uitgevoerd in Skia, waarbij we FreeType-rendering vergeleken met Skrifa- en Skia-rendering om ervoor te zorgen dat de pixelverschillen absoluut minimaal zijn, in alle vereiste weergavemodi (in verschillende anti-aliasing- en hinting-modi).

Fuzz-testen zijn een belangrijk hulpmiddel om te bepalen hoe een stukje software zal reageren op verkeerd opgemaakte en kwaadaardige invoer. Sinds juni 2024 zijn we voortdurend bezig met het ontwikkelen van onze nieuwe code. Dit heeft betrekking op de Rust-bibliotheken zelf en de integratiecode. Hoewel de fuzzer (op het moment van schrijven) 39 bugs heeft gevonden, is het vermeldenswaard dat geen van deze beveiligingskritisch is . Ze kunnen ongewenste visuele resultaten of zelfs gecontroleerde crashes veroorzaken, maar zullen niet leiden tot exploiteerbare kwetsbaarheden.

Voorwaarts!

We zijn erg blij met de resultaten van onze inspanningen om Rust voor tekst te gebruiken. Het leveren van veiligere code aan gebruikers en het verhogen van de productiviteit van ontwikkelaars is een enorme overwinning voor ons. We zijn van plan te blijven zoeken naar mogelijkheden om Rust in onze tekststapels te gebruiken. Als je meer wilt weten, schetst Oxidize enkele toekomstplannen van Google Fonts.