תוסף לתיוג זיכרון

Arm v9 מציגה את Arm Memory תוסף תיוג (MTE), הטמעת חומרה של זיכרון מתויג.

ברמה הכללית, MTE מתייג כל הקצאה/הקצאת זיכרון באמצעות מטא-נתונים נוספים. היא מקצה תג למיקום זיכרון, ואז יכול להיות שקשורה להצביע שמזכירים את המיקום הזה בזיכרון. בזמן ריצה, המעבד (CPU) בודק שהסמן ותגי המטא-נתונים תואמים בכל טעינה וחנות.

ב-Android 12, הקצאת זיכרון ערימה של ערימה (kernel) ומרחב משתמשים יכולה להרחיב לכל הקצאה עם מטא נתונים. פעולה זו עוזרת לזהות מקרים של שימוש לאחר השימוש באגים של גלישת נתונים זמניים, שהם המקור הנפוץ ביותר לבאגים של אבטחת זיכרון את מסדי הקוד שלנו.

מצבי פעילות של MTE

ל-MTE יש שלושה מצבי פעילות:

  • מצב סינכרוני (SYNC)
  • מצב אסינכרוני (ASYNC)
  • מצב אסימטרי (ASYMM)

מצב סינכרוני (SYNC)

המצב הזה עבר אופטימיזציה ונכונות של זיהוי באגים בנוגע לביצועים יכול לשמש ככלי מדויק לזיהוי באגים, כשיש תקורה גבוהה יותר של ביצועים מקובל עליי. כשהאפשרות הזו מופעלת, MTE SYNC פועל כצמצום אבטחה. במקרה של אי התאמה בין תגים, המעבד מבטל את ההפעלה באופן מיידי מסיים את התהליך באמצעות SIGSEGV (קוד SEGV_MTESERR) ומידע מלא על הגישה לזיכרון ועל הכתובת שבה נתקלת בבעיה.

מומלץ להשתמש במצב הזה במהלך הבדיקה כחלופה HWASan/KASAN או בייצור כאשר תהליך היעד מייצג שטח מתקפה. בנוסף, כאשר מצב ASYNC מציין נוכחות של באג, ניתן לקבל דוח מדויק על באג באמצעות ממשקי ה-API של סביבת זמן הריצה במצב 'סנכרון'.

כשפועלים במצב סנכרון, ההקצאה ל-Android מתעדת דוחות קריסות לכל המשתמשים הקצאות ותשלומים משתמשת בהם כדי לספק דוחות שגיאה טובים יותר שכוללים הסבר על זיכרון כגון use-after-free או buffer-overflow, ואת דוחות הקריסות אירועי זיכרון רלוונטיים. דוחות כאלה מספקים יותר מידע הקשרי כדי שיהיה קל יותר לאתר באגים ולתקן אותם.

מצב אסינכרוני (ASYNC)

המצב הזה עבר אופטימיזציה לשיפור רמת הדיוק של דוחות על באגים, והוא יכול יכול לשמש כזיהוי של תקורה נמוכה מדי עבור באגים שקשורים לבטיחות הזיכרון.
במקרה של אי התאמה בין תגים, המעבד ממשיך לבצע עד רשומת ליבה (לדוגמה, הפרעה מסוג syscall או הפרעה בטיימר), כאשר היא מסתיימת התהליך עם SIGSEGV (קוד SEGV_MTEAERR) בלי ומתעדים את הכתובת הבעייתית או את הגישה לזיכרון.
מומלץ להשתמש במצב הזה בסביבת הייצור בבסיסי קוד שנבדקו היטב, שבהם ידוע שצפיפות הבאגים של אבטחת הזיכרון היא נמוכה, וזה מושג באמצעות את מצב הסנכרון במהלך הבדיקה.

מצב אסימטרי (ASYMM)

מצב MTE אסימטרי הוא תכונה נוספת ב- Arm v8.7-A שמספק סנכרון סינכרוני. בדיקת קריאות זיכרון ובדיקה אסינכרונית של כתיבות זיכרון, לביצועים שדומים לאלו של מצב ASYNC. ברוב המקרים, הוא שיפור במצב ASYNC, ומומלץ להשתמש בו במקום כדאי לבצע סנכרון בכל פעם שהוא זמין.

לכן, באף אחד מממשקי ה-API שמתוארים בהמשך אין אזכור אסימטרי במצב תצוגה. במקום זאת, ניתן להגדיר את מערכת ההפעלה להשתמש תמיד במצב אסימטרי כאשר נשלחת בקשה אסינכרונית. אפשר לקרוא מידע נוסף בקטע 'הגדרת התצורה של המעבד (CPU) הספציפי רמת MTE מועדפת לקבלת מידע נוסף.

MTE במרחב המשתמשים

בקטעים הבאים מוסבר איך אפשר להפעיל MTE לתהליכי מערכת ואפליקציות. MTE מושבת כברירת מחדל, אלא אם אחת מהאפשרויות הבאות מוגדרת לתהליך מסוים (בהמשך מוסבר אילו רכיבים MTE מופעל).

הפעלת MTE באמצעות מערכת ה-build

כנכס ברמת התהליך, MTE נשלט על ידי הגדרת זמן ה-build של בקובץ ההפעלה הראשי. האפשרויות הבאות מאפשרות לשנות את ההגדרה הזו עבור בקובצי הפעלה בודדים, או לספריות משנה שלמות בעץ המקור. מתעלמת מההגדרה הזו בספריות, או בכל יעד שאינו קובץ הפעלה או לבדיקה.

1. הפעלת MTE ב-Android.bp (דוגמה), לפרויקט מסוים:

מצב MTE הגדרה
MTE אסינכרוני
  sanitize: {
  memtag_heap: true,
  }
MTE סינכרוני
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

או ב-Android.mk:

מצב MTE הגדרה
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. הפעלת MTE בספריית משנה בעץ המקור באמצעות מוצר משתנה:

מצב MTE הכללת רשימה החרגת הרשימה
אסינכרוני PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
סנכרון PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS

או

מצב MTE הגדרה
MTE אסינכרוני MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MTE סינכרוני MEMTAG_HEAP_SYNC_INCLUDE_PATHS

או על ידי ציון הנתיב להחרגה של קובץ הפעלה:

מצב MTE הגדרה
MTE אסינכרוני PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
MTE סינכרוני

דוגמה, (שימוש דומה ל-PRODUCT_CFI_INCLUDE_PATHS)

  PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
  PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
                                    vendor/$(vendor)/projectB

הפעלת MTE באמצעות מאפייני מערכת

אפשר לשנות את הגדרות ה-build שלמעלה בזמן הריצה מאפיין המערכת הבא:

arm64.memtag.process.<basename> = (off|sync|async)

כאשר basename מייצג את שם הבסיס של קובץ ההפעלה.

לדוגמה, כדי להגדיר /system/bin/ping או /data/local/tmp/ping כדי להשתמש ב-MTE אסינכרוני, יש להשתמש ב-adb shell setprop arm64.memtag.process.ping async.

הפעלת MTE באמצעות משתנה סביבה

דרך נוספת לשנות את הגדרת ה-build היא להגדיר את הסביבה משתנה: MEMTAG_OPTIONS=(off|sync|async) אם גם משתנה הסביבה וגם מאפיין המערכת מוגדרים, מקבל קדימות.

הפעלת MTE לאפליקציות

אם לא צוין MTE מושבת כברירת מחדל, כדי לעשות זאת, צריך להגדיר את android:memtagMode לאפליקציות שרוצים להשתמש ב-MTE מתחת ל-<application> או תג <process> ב AndroidManifest.xml

android:memtagMode=(off|default|sync|async)

כשהוא מוגדר בתג <application>, המאפיין משפיע על כל התהליכים שהאפליקציה משתמשת בו, ואפשר לשנות אותו לתהליכים נפרדים על ידי הגדרת תג <process>.

לניסוי, תאימות כדי להגדיר את ערך ברירת המחדל memtagMode לאפליקציה שמאפשרת לא לציין ערך כלשהו במניפסט (או מציין default).
ניתן למצוא את ההגדרות האלה בקטע System > Advanced > Developer options > App Compatibility Changes בתפריט ההגדרות הגלובלי. ההגדרה NATIVE_MEMTAG_ASYNC או NATIVE_MEMTAG_SYNC מפעילים MTE לאפליקציה מסוימת.
לחלופין, אפשר להגדיר את האפשרות הזו באמצעות הפקודה am כך:

$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name

יצירת קובץ אימג' של מערכת MTE

מומלץ מאוד להפעיל MTE בכל הקבצים הבינאריים המקוריים במהלך הפיתוח ועולה. כך ניתן לזהות באגים של בטיחות זיכרון בשלב מוקדם ולספק נתונים מציאותיים כיסוי המשתמשים, אם הוא מופעל בגרסת build לבדיקה.

מומלץ מאוד להפעיל MTE במצב סינכרוני בכל הקבצים הבינאריים המקוריים במהלך הפיתוח

SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m

כמו כל משתנה במערכת ה-build, SANITIZE_TARGET יכול להיות שמשמש כמשתנה סביבה או כהגדרה של make (לדוגמה, product.mk קובץ).
שימו לב: הפעולה הזו מפעילה את MTE בכל התהליכים המקומיים, אבל לא עבור (שפוצלו מ-zygote64) שאפשר להפעיל עבורן MTE מופעל לפי ההוראות שלמעלה.

הגדרת רמת ה-MTE המועדפת שספציפית למעבד (CPU)

במעבדי CPU מסוימים, הביצועים של MTE ב-ASYMM או אפילו במצבי SYNC עשויים להיות דומים לביצועים של ASYNC. לכן כדאי להפעיל בדיקות מחמירות יותר על המעבדים האלה כשנשלחת בקשה למצב בדיקה פחות מחמיר, כדי להפיק את יתרונות זיהוי השגיאות של בדיקות מחמירות יותר והחסרונות שלה.
כברירת מחדל, תהליכים שהוגדרו לפעול במצב ASYNC יפעלו ב-ASYNC בכל המעבדים (CPU). להגדיר שהליבה תריץ את התהליכים האלה במצב SYNC ב- מעבדים ספציפיים, צריך לכתוב את סנכרון הערכים ערך אחד (sysfs) /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred בזמן ההפעלה בזמן האימון. אפשר לעשות את זה באמצעות סקריפט ראשוני. לדוגמה, כדי להגדיר מעבדים 0-1 כדי להריץ תהליכים במצב ASYNC במצב SYNC (סנכרון) ומעבדים 2-3 (מעבדים 2-3) שמיועדים להרצה במצב ASYMM. אפשר להוסיף את הפרטים הבאים לסעיף הכניסה של סקריפט אתחול של ספק:

  write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm
  write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm

מצייני מיקום מתהליכי מצב ASYNC שפועלים במצב SYNC יכילו דוח קריסות מדויק למיקום של שגיאת הזיכרון. עם זאת, הם לא כולל דוח קריסות של הקצאה או מיקום עסקה. דוחות הקריסות האלה בלבד זמינה אם התהליך מוגדר לרוץ במצב 'סנכרון'.

int mallopt(M_THREAD_DISABLE_MEM_INIT, level)

כאשר level הוא 0 או 1.
השבתת אתחול הזיכרון ב-Malloc והימנעות משינוי תגי זיכרון אלא אם הדבר נחוץ לתיקון הבעיה.

int mallopt(M_MEMTAG_TUNING, level)

כאשר level הוא:

  • M_MEMTAG_TUNING_BUFFER_OVERFLOW
  • M_MEMTAG_TUNING_UAF

בחירה של אסטרטגיית הקצאת תגים

  • הגדרת ברירת המחדל היא M_MEMTAG_TUNING_BUFFER_OVERFLOW.
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW – מאפשר דטרמיניסטי זיהוי של צבירת נתונים זמניים וחריגים בזרימה לינארית על ידי הקצאת תג נפרד להקצאות סמוכות. במצב הזה יש סיכוי קטן קצת מזהים באגים שמתרחשים לאחר השימוש בחינם, כי רק מחצית מערכי התגים האפשריים לכל מיקום בזיכרון. חשוב לזכור ש-MTE לא יכול לזהות גולשים בתוך אותו חלקיק תג (מקטע מיושר של 16 בייטים), ועלולים לפספס פרטים קטנים גולשים גם במצב הזה. גלישה כזו לא יכולה להיות הסיבה לזיכרון שחיתות, כי הזיכרון בתוך חלקיק אחד אף פעם לא משמש והקצאות אחרות.
  • M_MEMTAG_TUNING_UAF - הפעלת תגים אקראיים באופן עצמאי להסתברות אחידה של כ-93% לזיהוי המרחבי (גלישת מאגר נתונים זמני) וגם באגים זמניים (שימוש לאחר חינם).

בנוסף לממשקי ה-API שתוארו למעלה, ייתכן שמשתמשים מנוסים ירצו לשים לב לדברים הבאים:

  • ניתן להגדיר את רישום החומרה של PSTATE.TCO באופן זמני ביטול בדיקת התגים (דוגמה). לדוגמה, כשמעתיקים טווח של זיכרון עם תוכן תג לא ידוע, או לטיפול בצוואר בקבוק בביצועים בלולאה חמה.
  • כשמשתמשים ב-M_HEAP_TAGGING_LEVEL_SYNC, ה-handler של קריסת המערכת שמספק מידע נוסף, כמו דוחות קריסות של הקצאות ומיקום עסקאות. לפונקציונליות הזו נדרשת גישה לביטים של התג, והיא מופעלת על ידי העברת SA_EXPOSE_TAGBITS כשמגדירים את ה-handler של האותות. כל תוכנית שמגדירה אות משלה handler ונותנים למשתמשים קריסות לא מוכרות למערכת. מומלץ לעשות את זה זהה.

MTE בליבה (kernel)

כדי להפעיל KASAN עם האצת MTE בשביל הליבה, להגדיר את הליבה עם CONFIG_KASAN=y, CONFIG_KASAN_HW_TAGS=y. ההגדרות האלה מופעלות כברירת מחדל בליבות של GKI, החל מ-Android 12-5.10.
אפשר לשלוט בכך בזמן האתחול באמצעות הארגומנטים הבאים בשורת הפקודה:

  • kasan=[on|off] - הפעלה או השבתה של KASAN (ברירת המחדל: on)
  • kasan.mode=[sync|async] – בחירה בין מצב סינכרוני למצב אסינכרוני (ברירת מחדל: sync)
  • kasan.stacktrace=[on|off] – האם לאסוף דוחות קריסות (ברירת המחדל: on)
    • כדי לאסוף גם דוחות קריסות stack_depot_disable=off
  • kasan.fault=[report|panic] - האם להדפיס רק את הדוח, או גם להפעיל פאניקה את הליבה (ברירת מחדל: report). בלי קשר , בדיקת התגים מושבתת לאחר השגיאה הראשונה המדווחת.

מומלץ מאוד להשתמש במצב סנכרון במהלך העלאת התוכן, הפיתוח בדיקה. צריך להפעיל את האפשרות הזו באופן גלובלי לכל התהליכים שמשתמשים במשתנה הסביבה או באמצעות מערכת ה-build. במצב הזה המערכת מזהה באגים בשלב מוקדם של תהליך הפיתוח, ה-codebase מייצב מהר יותר המערכת מונעת עלות של זיהוי באגים בשלב מאוחר יותר בסביבת הייצור.

מומלץ מאוד להשתמש במצב ASYNC בסביבת הייצור. זה מספק כלי במבט-על לזיהוי הנוכחות של באגים הקשורים לבטיחות הזיכרון בתהליך, וגם הגנה נוספת לעומק. לאחר זיהוי באג, המפתח יכול להשתמש בממשקי API של סביבת זמן הריצה כדי לעבור למצב סנכרון ולקבל דוח קריסות מדויק. מתוך קבוצה שנדגמה של משתמשים.

מומלץ מאוד להגדיר את רמת ה-MTE המועדפת שספציפית למעבד (CPU) ה-SoC. למצב אסימציית בדרך כלל יש מאפייני ביצועים זהים לאלה של ASYNC, וכמעט תמיד הוא עדיף. ליבות קטנות לפי הסדר בדרך כלל דומות הביצועים בכל שלושת המצבים, וניתן להגדיר אותם כך שתעדיפו סנכרון.

על המפתחים לבדוק אם יש קריסות על ידי בדיקה /data/tombstones, logcat או על ידי מעקב אחר הספק DropboxManager צינור עיבוד נתונים לבאגים על משתמשי קצה. למידע נוסף על ניפוי באגים בקוד הנייטיב של Android, אפשר להיכנס לאתר המידע זמין כאן.

רכיבי פלטפורמה שתומכים ב-MTE

ב-Android 12, כמה רכיבי מערכת קריטיים של אבטחה משתמשים ב-MTE ASYNC לזיהוי קריסות של משתמשי קצה ולשמש כשכבה נוספת הגנה לעומק. אלו הם הרכיבים:

  • רשתות דימון (daemon) ותשתיות (למעט netd)
  • Bluetooth, SecureElement, NFC HAL ואפליקציות מערכת
  • דימון (statsd)
  • system_server
  • zygote64 (כדי לאפשר לאפליקציות להביע הסכמה לשימוש ב-MTE)

היעדים האלה נבחרו על סמך הקריטריונים הבאים:

  • תהליך בעל הרשאות (מוגדר כתהליך שיש לו גישה למשהו שדומיין SELinux לא מורשה (unprivileged_app)
  • קלט לא מהימן של תהליכים (כלל מתוך שתיים)
  • האטה סבירה בביצועים (האטה לא גורמת להצגה של המשתמש זמן אחזור)

אנחנו מעודדים ספקים להפעיל MTE בסביבת ייצור עבור רכיבים נוספים, לעמוד בקריטריונים שצוינו למעלה. במהלך הפיתוח, מומלץ לבצע בדיקה מהרכיבים האלו באמצעות מצב 'סנכרון', כדי לזהות באגים שתוקנו בקלות ולהעריך סנכרון ההשפעה על הביצועים שלהם.
בעתיד, התוכנית של Android תרחיב את רשימת רכיבי המערכת MTE מופעל, בהתאם למאפייני הביצועים של עיצובי חומרה עתידיים.