Dipublikasikan: 19 Maret 2025
Skrifa ditulis dalam Rust, dan dibuat sebagai pengganti FreeType untuk membuat pemrosesan font di Chrome aman bagi semua pengguna kami. Skrifa memanfaatkan keamanan memori Rust, dan memungkinkan kami melakukan iterasi lebih cepat pada peningkatan teknologi font di Chrome. Beralih dari FreeType ke Skrifa memungkinkan kami menjadi lebih gesit dan berani saat membuat perubahan pada kode font kami. Sekarang kami menghabiskan waktu yang jauh lebih sedikit untuk memperbaiki bug keamanan, sehingga menghasilkan update yang lebih cepat, dan kualitas kode yang lebih baik.
Postingan ini menjelaskan alasan Chrome tidak lagi menggunakan FreeType, dan beberapa detail teknis menarik tentang peningkatan yang dihasilkan dari perubahan ini.
Mengapa FreeType diganti?
Web unik karena memungkinkan pengguna mengambil resource yang tidak tepercaya dari berbagai sumber yang tidak tepercaya dengan harapan semuanya akan berfungsi dan aman. Asumsi ini umumnya benar, tetapi menepati janji tersebut kepada pengguna memerlukan biaya. Misalnya, untuk menggunakan font web dengan aman (font yang dikirimkan melalui jaringan), Chrome menerapkan beberapa mitigasi keamanan:
- Pemrosesan font di-sandbox sesuai dengan aturan dua: font tidak tepercaya dan kode yang menggunakannya tidak aman.
- Font diteruskan melalui OpenType Sanitizer sebelum diproses.
- Semua library yang terlibat dalam mendekompresi dan memproses font telah diuji fuzz.
Chrome dilengkapi dengan FreeType dan menggunakannya sebagai library pemrosesan font utama di Android, ChromeOS, dan Linux. Artinya, banyak pengguna yang terpapar jika ada kerentanan di FreeType.
Library FreeType digunakan oleh Chrome untuk menghitung metrik dan memuat garis luar yang diberi petunjuk dari font. Secara keseluruhan, penggunaan FreeType telah memberikan manfaat besar bagi Google. Kami sangat mengandalkan dan berkontribusi kembali pada alat ini karena alat ini melakukan pekerjaan yang kompleks dengan baik. Namun, kode ini ditulis dalam kode yang tidak aman dan berasal dari masa ketika input berbahaya lebih jarang terjadi. Sekadar mengikuti aliran masalah yang ditemukan oleh fuzzing saja membutuhkan biaya setidaknya 0,25 FTE (Full Time Equivalent) untuk engineer software Google. Lebih buruk lagi, kami jelas tidak menemukan semuanya atau hanya menemukan sesuatu setelah kode dikirimkan kepada pengguna.
Pola masalah ini tidak hanya terjadi pada FreeType. Kami mengamati bahwa library tidak aman lainnya juga mengalami masalah meskipun kami menggunakan software engineer terbaik yang dapat kami temukan, meninjau setiap perubahan kode, dan mewajibkan pengujian.
Mengapa masalah terus muncul
Saat mengevaluasi keamanan FreeType, kami mengamati tiga kelas masalah utama yang terjadi (tidak lengkap):
Penggunaan bahasa yang tidak aman
| Pola/Masalah | Contoh |
|---|---|
| Pengelolaan memori manual |
|
| Akses array yang tidak diperiksa | CVE-2022-27404 |
| Overflow bilangan bulat | Selama eksekusi mesin virtual tersemat untuk pemberian petunjuk TrueType pada gambar dan pemberian petunjuk CFF https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
| Penggunaan alokasi nol versus non-nol yang salah | Diskusi di https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94, 8 masalah fuzzer ditemukan setelahnya |
| Pemeran tidak valid | Lihat baris berikut tentang penggunaan makro |
Masalah khusus project
| Pola/Masalah | Contoh |
|---|---|
| Makro menyamarkan kurangnya pengetikan ukuran eksplisit |
|
| Kode baru secara konsisten menambahkan bug, bahkan saat ditulis secara defensif. |
|
| Kurangnya pengujian |
|
Masalah dependensi
Fuzzing telah berulang kali mengidentifikasi masalah di library yang menjadi dependensi FreeType, seperti bzip2, libpng, dan zlib. Sebagai contoh, bandingkan freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate.
Fuzzing tidak cukup
Fuzzing–pengujian otomatis dengan berbagai input, termasuk input tidak valid yang diacak–dimaksudkan untuk menemukan banyak jenis masalah yang masuk ke rilis stabil Chrome. Kami melakukan fuzzing FreeType sebagai bagian dari project oss-fuzz Google. Masalah memang ditemukan, tetapi font terbukti agak tahan terhadap fuzzing karena alasan berikut.
File font bersifat kompleks, sebanding dengan file video karena berisi beberapa jenis informasi yang berbeda. File font adalah format penampung untuk beberapa tabel, dengan setiap tabel memiliki tujuan yang berbeda dalam memproses teks dan font secara bersamaan untuk menghasilkan glif yang diposisikan dengan benar di layar. Dalam file font, Anda akan menemukan:
- Metadata statis seperti nama font dan parameter untuk font variabel.
- Pemetaan dari karakter Unicode ke glif.
- Set aturan dan tata bahasa yang kompleks untuk tata letak layar glyph.
- Informasi visual: Bentuk glyph dan informasi gambar yang menjelaskan tampilan glyph yang ditempatkan di layar.
- Tabel visual pada gilirannya dapat menyertakan program hinting TrueType, yang merupakan program mini yang dijalankan untuk mengubah bentuk glyph.
- String karakter dalam tabel CFF atau CFF2 yang merupakan kurva imperatif petunjuk penggambaran dan hinting yang dieksekusi di mesin rendering CFF.
Ada kompleksitas dalam file font yang setara dengan memiliki bahasa pemrograman dan pemrosesan mesin statusnya sendiri, yang memerlukan virtual machine tertentu untuk mengeksekusinya.
Karena kompleksitas formatnya, fuzzing memiliki kekurangan dalam menemukan masalah dalam file font.
Cakupan kode yang baik atau progres fuzzer sulit dicapai karena alasan berikut:
- Fuzzing program hinting TrueType, string karakter CFF, dan tata letak OpenType menggunakan mutator gaya pembalikan/pergeseran/penyisipan/penghapusan bit sederhana berjuang untuk mencapai semua kombinasi status.
- Fuzzing setidaknya harus menghasilkan struktur yang sebagian valid. Mutasi acak jarang melakukannya, sehingga sulit untuk mencapai cakupan yang baik, terutama untuk tingkat kode yang lebih dalam.
- Upaya fuzzing saat ini di ClusterFuzz dan oss-fuzz belum menggunakan mutasi yang sadar struktur. Penggunaan mutator yang memahami tata bahasa atau struktur dapat membantu menghindari produksi varian yang ditolak lebih awal, dengan biaya yang lebih besar untuk waktu pengembangan, dan memperkenalkan peluang yang melewatkan bagian dari ruang penelusuran.
Data di beberapa tabel harus disinkronkan agar fuzzing dapat berjalan:
- Pola mutasi fuzzers yang biasa tidak menghasilkan data yang sebagian valid sehingga banyak iterasi ditolak dan progres menjadi lambat.
- Pemetaan glyph, tabel tata letak OpenType, dan gambar glyph saling terhubung dan bergantung satu sama lain, membentuk ruang kombinatorial yang sudutnya sulit dijangkau dengan fuzzing.
- Misalnya, kerentanan tt_face_get_paint COLRv1 dengan tingkat keparahan tinggi membutuhkan waktu lebih dari 10 bulan untuk ditemukan.
Terlepas dari upaya terbaik kami, masalah keamanan font telah berulang kali mencapai pengguna akhir. Mengganti FreeType dengan alternatif Rust akan mencegah beberapa kelas kerentanan secara keseluruhan.
Skrifa di Chrome
Skia adalah library grafis yang digunakan oleh Chrome. Skia mengandalkan FreeType untuk memuat metadata dan bentuk huruf dari font. Skrifa adalah library Rust, bagian dari rangkaian library Fontations, yang menyediakan pengganti yang aman untuk bagian FreeType yang digunakan oleh Skia.
Untuk mentransisikan FreeType ke Skia, tim Chrome mengembangkan backend font Skia baru berdasarkan Skrifa dan secara bertahap meluncurkan perubahan tersebut kepada pengguna:
- Di Chrome 128 (Agustus 2024), kami mengaktifkan Fontations untuk digunakan dalam format font yang kurang umum, seperti untuk font warna dan CFF2, sebagai uji coba yang aman.
- Di Chrome 133 (Februari 2025), kami mengaktifkan Fontations untuk semua penggunaan font web di Linux, Android, dan ChromeOS, serta untuk penggunaan font web sebagai fallback di Windows dan Mac—dalam kasus saat sistem tidak mendukung format font, tetapi Chrome perlu menampilkannya.
Untuk integrasi ke Chrome, kami mengandalkan integrasi Rust yang lancar ke dalam codebase yang diperkenalkan oleh tim keamanan Chrome.
Pada masa mendatang, kami juga akan beralih ke Fontations untuk font sistem operasi, dimulai dengan Linux dan ChromeOS, lalu di Android.
Keselamatan, yang paling utama
Tujuan utama kami adalah mengurangi (atau idealnya, menghilangkan!) kerentanan keamanan yang disebabkan oleh akses di luar batas ke memori. Rust menyediakan hal ini secara langsung selama Anda menghindari blok kode tidak aman.
Tujuan performa kami mengharuskan kami melakukan satu operasi yang saat ini tidak aman: menafsirkan ulang byte arbitrer sebagai struktur data yang sangat diketik. Hal ini memungkinkan kita membaca data dari file font tanpa melakukan penyalinan yang tidak perlu dan penting untuk menghasilkan parser font yang cepat.
Untuk menghindari kode tidak aman kami sendiri, kami memilih untuk mengalihdayakan tanggung jawab ini ke bytemuck yang merupakan library Rust yang didesain khusus untuk tujuan ini dan telah diuji serta digunakan secara luas di seluruh ekosistem. Memusatkan penafsiran ulang data mentah di bytemuck memastikan kita memiliki fungsi ini di satu tempat dan diaudit, serta menghindari pengulangan kode yang tidak aman untuk tujuan tersebut. safe transmute project bertujuan untuk menggabungkan fungsi ini langsung ke dalam compiler Rust dan kami akan melakukan peralihan segera setelah tersedia.
Ketepatan itu penting
Skrifa dibuat dari komponen independen yang sebagian besar struktur datanya dirancang agar tidak dapat diubah. Hal ini meningkatkan keterbacaan, kemudahan pemeliharaan, dan multithreading. Hal ini juga membuat kode lebih mudah diuji unit. Kami telah memanfaatkan peluang ini dan telah menghasilkan sekitar 700 pengujian unit yang mencakup seluruh stack kami dari rutin parsing tingkat rendah hingga mesin virtual hinting tingkat tinggi.
Ketepatan juga menyiratkan fidelitas dan FreeType sangat dihargai karena kemampuannya menghasilkan garis luar berkualitas tinggi. Kami harus mencocokkan kualitas ini agar menjadi pengganti yang sesuai. Untuk itu, kami telah membuat alat khusus bernama fauntlet yang membandingkan output Skrifa dan FreeType untuk batch file font di berbagai konfigurasi. Hal ini memberi kami jaminan bahwa kami dapat menghindari regresi kualitas.
Selain itu, sebelum integrasi ke Chromium, kami menjalankan berbagai perbandingan piksel di Skia, membandingkan rendering FreeType dengan rendering Skrifa dan Skia untuk memastikan perbedaan piksel benar-benar minimal, di semua mode rendering yang diperlukan (di berbagai mode anti-aliasing dan hinting).
Pengujian fuzz adalah alat penting untuk menentukan cara software akan bereaksi terhadap input yang salah format dan berbahaya. Kami terus melakukan fuzzing pada kode baru kami sejak Juni 2024. Hal ini mencakup library Rust itu sendiri dan kode integrasi. Meskipun fuzzer telah menemukan (hingga saat penulisan ini) 39 bug, perlu diperhatikan bahwa tidak satu pun di antaranya yang bersifat kritis dari segi keamanan. Perubahan ini dapat menyebabkan hasil visual yang tidak diinginkan atau bahkan error yang terkontrol, tetapi tidak akan menyebabkan kerentanan yang dapat dieksploitasi.
Lanjutkan!
Kami sangat puas dengan hasil upaya kami menggunakan Rust untuk teks. Menyediakan kode yang lebih aman bagi pengguna dan meningkatkan produktivitas developer adalah kemenangan besar bagi kami. Kami berencana untuk terus mencari peluang penggunaan Rust dalam stack teks kami. Jika Anda ingin mengetahui lebih lanjut, Oxidize menguraikan beberapa rencana masa depan Google Fonts.