תאריך פרסום: 19 במרץ 2025
Skrifa נכתבה ב-Rust ונוצרה כתחליף ל-FreeType כדי להבטיח עיבוד בטוח של גופנים ב-Chrome לכל המשתמשים שלנו. Skifra מנצל את אבטחת הזיכרון של Rust ומאפשר לנו לבצע איטרציות מהר יותר בשיפורים של טכנולוגיית הגופנים ב-Chrome. המעבר מ-FreeType ל-Skrifa מאפשר לנו להיות גמישים ונועזים כשאנחנו מבצעים שינויים בקוד הגופן שלנו. עכשיו אנחנו משקיעים הרבה פחות זמן בתיקון באגים באבטחה, וכתוצאה מכך אנחנו יכולים לעדכן את הקוד מהר יותר ולשפר את איכותו.
בפוסט הזה נסביר למה הפסקנו להשתמש ב-FreeType ב-Chrome, ונציג כמה פרטים טכניים מעניינים על השיפורים שהמעבר הזה אפשר.
למה מחליפים את FreeType?
האינטרנט הוא ייחודי בכך שהוא מאפשר למשתמשים לאחזר משאבים לא מהימנים ממגוון רחב של מקורות לא מהימנים, מתוך הנחה שהכול פשוט יפעל ושהפעולה הזו בטוחה. ההנחה הזו נכונה בדרך כלל, אבל יש עלות לשמירה על ההבטחה הזו למשתמשים. לדוגמה, כדי להשתמש בגופן אינטרנט בצורה בטוחה (גופן שנשלח דרך הרשת), Chrome משתמש בכמה אמצעי אבטחה:
- עיבוד הגופנים מתבצע בארגז חול בהתאם לכלל של שניים: הם לא מהימנים והקוד שמשתמש בהם לא בטוח.
- הגופנים עוברים דרך כלי הניקוי של OpenType לפני העיבוד.
- כל הספריות שמעורבות בביטול דחיסה ובעיבוד של גופנים עוברות בדיקות fuzz.
Chrome מגיע עם FreeType ומשתמש בו כספריית העיבוד הראשית של הגופנים ב-Android, ב-ChromeOS וב-Linux. כלומר, הרבה משתמשים חשופים אם יש נקודת חולשה ב-FreeType.
ספריית FreeType משמשת את Chrome לחישוב מדדים ולטעינה של קווי מתאר עם רמזים מגופנים. באופן כללי, השימוש ב-FreeType היה יתרון עצום ל-Google. הוא מבצע משימה מורכבת, ומבצע אותה היטב. אנחנו מסתמכים עליו במידה רבה ומוסיפים לו תוכן. עם זאת, הוא נכתב בקוד לא בטוח והוא נוצר בתקופה שבה היה סיכוי נמוך יותר לקבלת קלט זדוני. רק כדי לעמוד בקצב של הבעיות שמתגלות באמצעות בדיקות fuzzing, Google צריכה לפחות 0.25 מהנדסי תוכנה במשרה מלאה. גרוע מכך, אנחנו לא מוצאים את כל הבעיות או שאנחנו מוצאים אותן רק אחרי שהקוד נשלח למשתמשים.
דפוס הבעיות הזה לא ייחודי ל-FreeType. אנחנו צופים בעיות בספריות לא בטוחות אחרות, גם כשאנחנו משתמשים במהנדסי התוכנה הכי טובים שאנחנו יכולים למצוא, בודקים את הקוד בכל שינוי ומחייבים בדיקות.
למה הבעיות ממשיכות להופיע
כשבדקנו את האבטחה של FreeType, זיהינו שלוש קטגוריות עיקריות של בעיות (לא מפורטות):
שימוש בשפה לא בטוחה
דפוס/בעיה | דוגמה |
---|---|
ניהול זיכרון ידני |
|
גישה לא מאומתת למערך | CVE-2022-27404 |
גלישות של מספרים שלמים | במהלך ההפעלה של מכונות וירטואליות מוטמעות לחיוויית TrueType של ציור והנחיה של CFF https://issues.oss-fuzz.com/issues?q=FreeType%20Integer-overflow |
שימוש שגוי בהקצאה עם אפסה לעומת שימוש שגוי בהקצאה ללא אפסה | דיון בנושא https://gitlab.freedesktop.org/freetype/freetype/-/merge_requests/94, 8 בעיות ב-fuzzer שנמצאו לאחר מכן |
הטמעות לא חוקיות | השורה הבאה מתייחסת לשימוש במאקרו |
בעיות ספציפיות לפרויקט
דפוס/בעיה | דוגמה |
---|---|
מאקרו מסתיר את היעדר הטקסט של הגודל |
|
קוד חדש מוסיף באגים באופן קבוע, גם אם הוא נכתב באופן הגנתי. |
|
חוסר בדיקות |
|
בעיות של יחסי תלות
בדיקות fuzzing זיהו שוב ושוב בעיות בספריות ש-FreeType תלויה בהן, כמו bzip2, libpng ו-zlib. לדוגמה, אפשר להשוות את freetype_bdf_fuzzer: Use-of-uninitialized-value in inflate.
בדיקת fuzzing לא מספיקה
בדיקות fuzzing – בדיקות אוטומטיות עם מגוון רחב של קלט, כולל קלט לא חוקי אקראי – נועדו לזהות הרבה מהסוגים של בעיות שמגיעות לגרסה היציבה של Chrome. אנחנו מבצעים בדיקות fuzz ב-FreeType כחלק מפרויקט oss-fuzz של Google. הוא אכן מוצא בעיות, אבל גופנים עמידים במידה מסוימת בפני בדיקות fuzzing, מהסיבות הבאות.
קובצי גופנים הם מורכבים, בדומה לקובצי וידאו, כי הם מכילים כמה סוגים שונים של מידע. קובצי גופנים הם פורמט מאגר של כמה טבלאות, שבהן כל טבלה משמשת למטרה שונה בעיבוד הטקסט והגופנים יחד כדי ליצור גליף שממוקם בצורה נכונה במסך. בקובץ גופן תוכלו למצוא:
- מטא-נתונים סטטיים כמו שמות גופנים ופרמטרים של גופנים משתנים.
- מיפויים מתווי Unicode לגליפים.
- מערכת כללים ודקדוק מורכבים לפריסה במסך של גליפים.
- מידע חזותי: צורות של גליפים ומידע על תמונות שמתארים את המראה של הגליפים שמופיעים במסך.
- הטבלאות החזוניות יכולות לכלול תוכניות TrueType hinting, שהן תוכניות מיני שמריצים כדי לשנות את צורת הגליף.
- מחרוזות תווים בטבלאות CFF או CFF2, שהן הוראות ציור של עקומות והנחיות לטישטוש (hinting) שמבוצעות במנוע הרינדור של CFF.
קובצי הגופנים מורכבים כמו שפה תכנותית משלהם ותהליך עיבוד של מכונת מצב, שנדרשות מכונות וירטואליות ספציפיות כדי להריץ אותם.
בגלל המורכבות של הפורמט, ל-fuzzing יש חסרונות במציאת בעיות בקובצי גופנים.
קשה להשיג כיסוי קוד טוב או התקדמות טובה של ה-fuzzer מהסיבות הבאות:
- כשמבצעים fuzzing לתוכניות של TrueType hinting, למחרוזות תווים של CFF ולפריסה של OpenType באמצעות מוטאטורים פשוטים מסוג היפוך ביט, הזזה, הוספה או מחיקה, קשה להגיע לכל השילובים של המצבים.
- בדיקת fuzzing צריכה לייצר לפחות מבנים תקינים חלקית. מוטציה אקראית עושה זאת לעיתים רחוקות, ולכן קשה להשיג כיסוי טוב, במיוחד ברמות קוד עמוקות יותר.
- בשלב הזה, המאמצים הנוכחיים לזיהוי באגים בקוד באמצעות בדיקות fuzzing ב-ClusterFuzz וב-oss-fuzz עדיין לא כוללים שימוש במוּטציה מבוססת-מבנה. שימוש במוטאציות שמתבססות על תחביר או מבנה יכול לעזור למנוע יצירת וריאנטים שנדחים בשלב מוקדם, אבל התהליך הזה ייקח יותר זמן, ויכול להיות שיגרום לפספס חלקים ממרחב החיפוש.
כדי שהבדיקה באמצעות פולסים (fuzzing) תתקדם, הנתונים בכמה טבלאות צריכים להיות מסונכרנים:
- דפוסי המוטציה הרגילים של כלי fuzzing לא יוצרים נתונים תקינים חלקית, ולכן הרבה חזרות נדחות וההתקדמות איטית.
- מיפוי הגליפים, טבלאות הפריסה של OpenType והציור של הגליפים מקושרים ותלויים זה בזה, ויוצרים מרחב משולב שקשה להגיע לפינות שלו באמצעות בדיקות fuzzing.
- לדוגמה, נדרשו יותר מ-10 חודשים כדי למצוא את נקודת החולשה tt_face_get_paintCOLRv1 ברמת חומרה גבוהה.
למרות שעשינו כמיטב יכולתנו, בעיות באבטחת הגופנים הגיעו שוב ושוב למשתמשי הקצה. החלפת FreeType בחלופה של Rust תמנע מספר סוגים של נקודות חולשה.
Skrifa ב-Chrome
Skia היא ספריית הגרפיקה שבה משתמש Chrome. Skia מסתמכת על FreeType כדי לטעון מטא-נתונים וצורות אותיות מגופנים. Skrifa היא ספרייה ב-Rust, ששייכת למשפחת הספריות Fontations, ומספקת תחליף בטוח לחלקים של FreeType שבהם Skia משתמשת.
כדי לעבור מ-FreeType ל-Skia, צוות Chrome פיתח קצוות עורפיים חדשים של גופנים ב-Skia שמבוססים על Skrifa, והשיק את השינוי למשתמשים באופן הדרגתי:
- ב-Chrome 128 (אוגוסט 2024) הפעלנו את Fontations לשימוש בפורמטים של גופנים פחות נפוצים, כמו גופנים צבעוניים ו-CFF2, כניסיון בטוח.
- ב-Chrome 133 (פברואר 2025) הפעלנו את Fontations לכל השימוש בגופנים באינטרנט ב-Linux, ב-Android וב-ChromeOS, וגם לשימוש בגופנים באינטרנט כחלופה ב-Windows וב-Mac – במקרים שבהם המערכת לא תומכת בפורמט של גופן אבל Chrome צריך להציג אותו.
לשילוב ב-Chrome, אנחנו מסתמכים על השילוב החלק של Rust בקוד הבסיסי שהציג צוות האבטחה של Chrome.
בעתיד נעבור ל-Fontation גם לגופנים של מערכת ההפעלה, תחילה ב-Linux וב-ChromeOS ולאחר מכן ב-Android.
בטיחות, קודם כול
המטרה העיקרית שלנו היא לצמצם (ואידיאלית, למנוע!) נקודות חולשה באבטחה שנגרמות בגלל גישה מחוץ למגבלות לזיכרון. Rust מספקת את האפשרות הזו כברירת מחדל, כל עוד אתם נמנעים מבלוק קוד לא בטוח.
כדי לעמוד ביעדי הביצועים שלנו, אנחנו צריכים לבצע פעולה אחת שכרגע לא בטוחה: פרשנות מחדש של בייטים שרירותיים כמבנה נתונים עם טיפוס חזק. כך אנחנו יכולים לקרוא את הנתונים מקובץ הגופן בלי לבצע עותקים מיותרים, והוא חיוני ליצירת מנתח גופנים מהיר.
כדי להימנע משימוש בקוד לא בטוח, בחרנו להעביר את האחריות הזו ל-bytemuck, ספריית Rust שתוכננה במיוחד למטרה הזו, שעברה בדיקות רבות ומשמשת את הסביבה העסקית שלנו. ריכוז הפרשנות מחדש של נתונים גולמיים ב-bytemuck מבטיח שהפונקציונליות הזו תהיה במקום אחד ותעבור ביקורת, ומאפשר לנו להימנע מהצגה חוזרת של קוד לא בטוח למטרה הזו. מטרת פרויקט safe transmute היא לשלב את הפונקציונליות הזו ישירות במהדר של Rust, ואנחנו נעביר את הקוד ברגע שהוא יהיה זמין.
הנכונות חשובה
Skrifa בנויה מרכיבים עצמאיים, שבהם רוב מבני הנתונים תוכננו כך שלא ניתן לשנות אותם. כך קל יותר לקרוא את הקוד, קל יותר לתחזק אותו וקל יותר להשתמש בו במספר תהליכים בו-זמנית. בנוסף, הקוד קל יותר לבדיקות יחידה. נצלנו את ההזדמנות הזו ויצאנו חבילה של כ-700 בדיקות יחידה שמכסות את כל הערימה שלנו, החל מרוטינות ניתוח ברמה נמוכה ועד למכונות וירטואליות עם רמזים ברמה גבוהה.
נכונות גם מתייחסת לדיוק, ו-FreeType נחשבת לספרייה מצוינת ליצירת קווים כלליים באיכות גבוהה. אנחנו צריכים להתאים לאיכות הזו כדי להציע לכם תמונה חלופית מתאימה. לשם כך, פיתחנו כלי מותאם אישית בשם fauntlet שמשויך לקבוצות של קובצי גופנים ומשויך לקבוצות של קובצי גופנים במגוון רחב של הגדרות, ומשויך לקבוצות של קובצי גופנים במגוון רחב של הגדרות. כך אנחנו יכולים להבטיח שנעמוד בסטנדרטים של איכות.
בנוסף, לפני השילוב ב-Chromium, הרצנו מגוון רחב של השוואות פיקסלים ב-Skia, בהשוואה בין העיבוד של FreeType לבין העיבוד של Skrifa ו-Skia, כדי לוודא שההבדלים בפיקסלים הם מינימליים ביותר בכל מצבי העיבוד הנדרשים (במגוון מצבי החלקה וטיפים).
בדיקת fuzz היא כלי חשוב שעוזר לקבוע איך קטע תוכנה יגיב לקלטים פגומים ותוכנות זדוניות. אנחנו מבצעים בדיקות fuzzing לקוד החדש שלנו באופן קבוע מאז יוני 2024. הבדיקה הזו כוללת את ספריות Rust עצמן ואת קוד השילוב. תוכנת ה-fuzzer מצאה (נכון למועד כתיבת המאמר) 39 באגים, אבל חשוב לציין שאף אחד מהם לא היה קריטי לאבטחה. הן עלולות לגרום לתוצאות חזותיות לא רצויות או אפילו לקריסות מבוקרות, אבל הן לא יובילו לנקודות חולשה שניתן לנצל.
המשך הדרך!
אנחנו מרוצים מאוד מהתוצאות של המאמצים שלנו להשתמש ב-Rust לטקסט. אנחנו מרוויחים מכך פעמיים: המשתמשים מקבלים קוד בטוח יותר והמפתחים נהנים מעלייה בפרודוקטיביות. אנחנו מתכננים להמשיך לחפש הזדמנויות לשימוש ב-Rust בסטים של הטקסט שלנו. למידע נוסף, תוכלו לקרוא את הפוסט Oxidize שבו מפורטים חלק מהתוכניות העתידיות של Google Fonts.