Veröffentlicht: 19. März 2025
Skrifa wurde in Rust geschrieben und als Ersatz für FreeType entwickelt, um die Schriftartenverarbeitung in Chrome für alle Nutzer sicherer zu machen. Skrifa nutzt die Speichersicherheit von Rust und ermöglicht es uns, schneller an Verbesserungen der Schriftarttechnologie in Chrome zu arbeiten. Durch den Wechsel von FreeType zu Skrifa können wir agil und furchtlos Änderungen an unserem Schriftartcode vornehmen. Wir verbringen jetzt viel weniger Zeit mit der Behebung von Sicherheitsfehlern, was zu schnelleren Updates und einer besseren Codequalität führt.
In diesem Beitrag wird erläutert, warum Chrome nicht mehr FreeType verwendet, und es werden einige interessante technische Details zu den Verbesserungen beschrieben, die dadurch möglich wurden.
Warum wird FreeType ersetzt?
Das Web ist insofern einzigartig, als Nutzer nicht vertrauenswürdige Ressourcen aus einer Vielzahl nicht vertrauenswürdiger Quellen abrufen können, in der Erwartung, dass alles funktioniert und dass sie dabei sicher sind. Diese Annahme ist im Allgemeinen richtig, aber die Einhaltung dieses Versprechens an die Nutzer ist mit Kosten verbunden. Wenn Sie beispielsweise eine Web-Schriftart (eine über das Netzwerk bereitgestellte Schriftart) sicher verwenden möchten, setzt Chrome mehrere Sicherheitsmaßnahmen ein:
- Die Schriftartverarbeitung erfolgt in einer Sandbox gemäß der Regel der zwei: Sie ist nicht vertrauenswürdig und der verwendende Code ist unsicher.
- Schriftarten werden vor der Verarbeitung durch den OpenType Sanitizer geleitet.
- Alle Bibliotheken, die zum Dekomprimieren und Verarbeiten von Schriftarten verwendet werden, wurden Fuzz-Tests unterzogen.
Chrome wird mit FreeType ausgeliefert und verwendet es als primäre Bibliothek für die Schriftartenverarbeitung unter Android, ChromeOS und Linux. Das bedeutet, dass viele Nutzer betroffen sind, wenn es eine Sicherheitslücke in FreeType gibt.
Die FreeType-Bibliothek wird von Chrome verwendet, um Messwerte zu berechnen und gerenderte Konturen aus Schriftarten zu laden. Insgesamt war die Verwendung von FreeType ein großer Erfolg für Google. Es erledigt eine komplexe Aufgabe gut, wir verlassen uns stark darauf und leisten einen Beitrag dazu. Sie ist jedoch in unsicherem Code geschrieben und stammt aus einer Zeit, in der böswillige Eingaben weniger wahrscheinlich waren. Allein die Behebung der durch Fuzzing gefundenen Probleme kostet Google mindestens 0,25 Vollzeit-Softwareentwickler. Schlimmer noch: Wir finden nicht alles oder erst, nachdem der Code an die Nutzer ausgeliefert wurde.
Dieses Muster von Problemen ist nicht auf FreeType beschränkt. Wir stellen fest, dass auch andere unsichere Bibliotheken Probleme aufweisen, selbst wenn wir die besten Softwareentwickler einsetzen, jede Änderung im Code überprüfen und Tests erforderlich sind.
Warum immer wieder Probleme auftreten
Bei der Bewertung der Sicherheit von FreeType haben wir drei Hauptklassen von Problemen beobachtet (nicht erschöpfend):
Verwendung einer unsicheren Sprache
| Muster/Problem | Beispiel |
|---|---|
| Manuelle Speicherverwaltung |
|
| Ungeprüfter Arrayzugriff | CVE-2022-27404 |
| Ganzzahlüberläufe | Während der Ausführung eingebetteter virtueller Maschinen für TrueType-Hinting von CFF-Zeichnungen und -Hinweisen https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
| Falsche Verwendung von Zuweisung mit und ohne Nullsetzung | Diskussion unter https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94, 8 Fuzzer-Probleme gefunden |
| Ungültige Besetzungen | Weitere Informationen zur Makronutzung finden Sie in der folgenden Zeile. |
Projektspezifische Probleme
| Muster/Problem | Beispiel |
|---|---|
| Makros verschleiern das Fehlen einer expliziten Größenangabe |
|
| Durch neuen Code werden immer wieder Fehler eingeführt, auch wenn er defensiv geschrieben ist. |
|
| Fehlende Tests |
|
Probleme mit Abhängigkeiten
Durch Fuzzing wurden wiederholt Probleme in Bibliotheken erkannt, von denen FreeType abhängt, z. B. bzip2, libpng und zlib. Ein Beispiel ist freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate.
Fuzzing reicht nicht aus
Beim Fuzzing werden automatisierte Tests mit einer Vielzahl von Eingaben durchgeführt, darunter auch zufällige ungültige Eingaben. So sollen viele der Probleme gefunden werden, die in die stabile Version von Chrome gelangen. Wir führen Fuzzing-Tests für FreeType im Rahmen des oss-fuzz-Projekts von Google durch. Es werden zwar Probleme gefunden, aber Schriftarten sind aus folgenden Gründen relativ resistent gegen Fuzzing:
Schriftartdateien sind komplex und mit Videodateien vergleichbar, da sie mehrere verschiedene Arten von Informationen enthalten. Schriftartdateien sind ein Containerformat für mehrere Tabellen, wobei jede Tabelle einen anderen Zweck bei der Verarbeitung von Text und Schriftarten hat, um ein korrekt positioniertes Glyphen auf dem Bildschirm zu erzeugen. Eine Schriftartdatei enthält:
- Statische Metadaten wie Schriftartnamen und Parameter für variable Schriftarten.
- Zuordnungen von Unicode-Zeichen zu Glyphen.
- Ein komplexer Regelsatz und eine komplexe Grammatik für das Bildschirm-Layout von Glyphen.
- Visuelle Informationen: Glyphenformen und Bildinformationen, die beschreiben, wie die auf dem Bildschirm platzierten Glyphen aussehen.
- Die visuellen Tabellen können wiederum TrueType-Hinting-Programme enthalten, die als Mini-Programme ausgeführt werden, um die Glyphenform zu ändern.
- Zeichenfolgen in den CFF- oder CFF2-Tabellen, die imperative Anweisungen zum Zeichnen von Kurven und zum Hinting enthalten, die in der CFF-Rendering-Engine ausgeführt werden.
Schriftartdateien sind komplex und haben eine eigene Programmiersprache und State Machine Processing, für die spezielle virtuelle Maschinen erforderlich sind.
Aufgrund der Komplexität des Formats hat Fuzzing Schwächen beim Auffinden von Problemen in Schriftartdateien.
Eine gute Codeabdeckung oder ein guter Fuzzer-Fortschritt ist aus folgenden Gründen schwierig zu erreichen:
- Beim Fuzzing von TrueType-Hinting-Programmen, CFF-Char-Strings und OpenType-Layouts mit einfachen Mutatoren vom Typ „Bit-Flipping“/„Shift“/„Insertion“/„Deletion“ (Bit-Umkehrung/Verschiebung/Einfügung/Löschung) ist es schwierig, alle Kombinationen von Status zu erreichen.
- Beim Fuzzing müssen zumindest teilweise gültige Strukturen erzeugt werden. Bei zufälligen Mutationen ist das selten der Fall, sodass eine gute Abdeckung schwer zu erreichen ist, insbesondere für tiefere Codeebenen.
- Beim aktuellen Fuzzing in ClusterFuzz und oss-fuzz wird noch keine strukturbezogene Mutation verwendet. Durch die Verwendung von Mutatoren, die auf Grammatik oder Struktur achten, lassen sich möglicherweise Varianten vermeiden, die frühzeitig abgelehnt werden. Das kann jedoch mehr Zeit in Anspruch nehmen und dazu führen, dass Teile des Suchraums nicht berücksichtigt werden.
Die Daten in mehreren Tabellen müssen synchronisiert werden, damit das Fuzzing voranschreiten kann:
- Die üblichen Mutationsmuster von Fuzzern erzeugen keine teilweise gültigen Daten. Daher werden viele Iterationen abgelehnt und der Fortschritt verlangsamt sich.
- Die Glyphenzuordnung, die OpenType-Layouttabellen und das Zeichnen von Glyphen sind miteinander verbunden und voneinander abhängig. Sie bilden einen kombinatorischen Raum, dessen Ecken mit Fuzzing schwer zu erreichen sind.
- So dauerte es beispielsweise mehr als 10 Monate, bis die Sicherheitslücke tt_face_get_paintCOLRv1 mit hohem Schweregrad gefunden wurde.
Trotz unserer Bemühungen sind Schriftart-Sicherheitsprobleme wiederholt bei Endnutzern aufgetreten. Wenn FreeType durch eine Rust-Alternative ersetzt wird, werden mehrere ganze Klassen von Sicherheitslücken verhindert.
Skrifa in Chrome
Skia ist die Grafikbibliothek, die von Chrome verwendet wird. Skia verwendet FreeType, um Metadaten und Glyphen aus Schriftarten zu laden. Skrifa ist eine Rust-Bibliothek, die zur Fontations-Bibliotheksfamilie gehört und einen sicheren Ersatz für die von Skia verwendeten Teile von FreeType bietet.
Um FreeType auf Skia umzustellen, hat das Chrome-Team ein neues Skia-Schriftart-Backend auf Grundlage von Skrifa entwickelt und die Änderung nach und nach für Nutzer eingeführt:
- In Chrome 128 (August 2024) haben wir Fontations für weniger häufig verwendete Schriftformate wie Farbschriften und CFF2 als sicheren Testlauf aktiviert.
- In Chrome 133 (Februar 2025) haben wir Fontations für alle Webfonts unter Linux, Android und ChromeOS sowie für die Verwendung von Webfonts als Fallback unter Windows und Mac aktiviert, wenn das System ein Schriftformat nicht unterstützt, Chrome es aber anzeigen muss.
Für die Integration in Chrome setzen wir auf die reibungslose Integration von Rust in die Codebasis, die vom Chrome-Sicherheitsteam eingeführt wurde.
Künftig werden wir auch für Betriebssystemschriftarten auf Fontations umstellen, beginnend mit Linux und ChromeOS, dann auf Android.
Sicherheit geht vor
Unser primäres Ziel ist es, Sicherheitslücken zu reduzieren (oder idealerweise zu beseitigen!), die durch den Zugriff auf Speicher außerhalb des zulässigen Bereichs verursacht werden. Rust bietet dies standardmäßig, solange Sie unsafe-Codeblöcke vermeiden.
Unsere Leistungsziele erfordern einen Vorgang, der derzeit unsicher ist: die Neuinterpretation beliebiger Bytes als stark typisierte Datenstruktur. So können wir die Daten aus einer Schriftartdatei lesen, ohne unnötige Kopien zu erstellen. Das ist wichtig, um einen schnellen Schriftartparser zu entwickeln.
Um eigenen unsicheren Code zu vermeiden, haben wir diese Verantwortung an bytemuck ausgelagert. Das ist eine Rust-Bibliothek, die speziell für diesen Zweck entwickelt wurde und im gesamten Ökosystem weit verbreitet ist. Durch die Konzentration der Neuinterpretation von Rohdaten in bytemuck wird sichergestellt, dass diese Funktion an einem Ort verfügbar ist und geprüft wird. Außerdem wird so vermieden, dass unsicherer Code für diesen Zweck wiederholt wird. Das safe transmute-Projekt zielt darauf ab, diese Funktion direkt in den Rust-Compiler zu integrieren. Wir werden die Umstellung vornehmen, sobald sie verfügbar ist.
Richtigkeit ist wichtig
Skrifa besteht aus unabhängigen Komponenten, wobei die meisten Datenstrukturen unveränderlich sind. Dies verbessert die Lesbarkeit, Wartbarkeit und Multithreading-Fähigkeit. Außerdem lässt sich der Code so besser unit-testen. Wir haben diese Gelegenheit genutzt und eine Reihe von etwa 700 Einheitentests erstellt, die unseren gesamten Stack abdecken, von Parsing-Routinen auf niedriger Ebene bis hin zu virtuellen Maschinen für das Hinting auf hoher Ebene.
Korrektheit bedeutet auch Genauigkeit und FreeType wird für die Erstellung hochwertiger Konturen sehr geschätzt. Wir müssen diese Qualität erreichen, um einen geeigneten Ersatz zu bieten. Dazu haben wir ein spezielles Tool namens fauntlet entwickelt, das die Ausgabe von Skrifa und FreeType für Batches von Schriftartdateien in einer Vielzahl von Konfigurationen vergleicht. So können wir sicher sein, dass wir keine Qualitätseinbußen haben.
Außerdem haben wir vor der Integration in Chromium eine Vielzahl von Pixelvergleichen in Skia durchgeführt, bei denen wir das FreeType-Rendering mit dem Skrifa- und Skia-Rendering verglichen haben, um sicherzustellen, dass die Pixeldifferenzen in allen erforderlichen Rendering-Modi (über verschiedene Antialiasing- und Hinting-Modi hinweg) absolut minimal sind.
Fuzzing ist ein wichtiges Tool, um zu ermitteln, wie Software auf fehlerhafte und schädliche Eingaben reagiert. Wir führen seit Juni 2024 kontinuierlich Fuzzing-Tests für unseren neuen Code durch. Das umfasst die Rust-Bibliotheken selbst und den Integrationscode. Der Fuzzer hat (Stand heute) 39 Bugs gefunden. Es ist jedoch wichtig zu wissen, dass keiner dieser Bugs sicherheitskritisch war. Sie können zu unerwünschten visuellen Ergebnissen oder sogar zu kontrollierten Abstürzen führen, aber nicht zu ausnutzbaren Sicherheitslücken.
Nur Mut!
Wir sind sehr zufrieden mit den Ergebnissen unserer Bemühungen, Rust für Text zu verwenden. Sichereren Code für Nutzer bereitzustellen und gleichzeitig die Produktivität von Entwicklern zu steigern, ist ein großer Vorteil für uns. Wir werden weiterhin nach Möglichkeiten suchen, Rust in unseren Text-Stacks einzusetzen. Weitere Informationen finden Sie unter Oxidize, wo einige der zukünftigen Pläne von Google Fonts dargelegt werden.