Arm v9 מציגה Arm Memory Tagging Extension (MTE), מימוש חומרה של זיכרון מתויג.
ברמה גבוהה, MTE מתייג כל הקצאת/הקצאת זיכרון במטא נתונים נוספים. הוא מקצה תג למיקום זיכרון, אשר לאחר מכן ניתן לשייך אותו למצביעים המתייחסים למיקום הזיכרון הזה. בזמן ריצה ה-CPU בודק שהמצביע ותגי המטא נתונים תואמים בכל טעינה ומאגר.
באנדרואיד 12, מקצה הזיכרון של הליבה ומרחב המשתמש יכול להגדיל כל הקצאה עם מטא נתונים. זה עוזר לזהות באגים ללא שימוש וללא גלישה, שהם המקור הנפוץ ביותר לבאגי בטיחות בזיכרון בבסיסי הקוד שלנו.
מצבי הפעלה של MTE
ל-MTE שלושה מצבי פעולה:
- מצב סינכרוני (SYNC)
- מצב אסינכרוני (ASYNC)
- מצב א-סימטרי (ASYMM)
מצב סינכרוני (SYNC)
מצב זה מותאם לנכונות זיהוי באגים על ביצועים וניתן להשתמש בו ככלי זיהוי באגים מדויק, כאשר תקורה של ביצועים גבוהים יותר מקובלת. כאשר מופעל, MTE SYNC פועל כאמצעי אבטחה. במקרה של חוסר התאמה בתג, המעבד מבטל את הביצוע באופן מיידי ומסיים את התהליך עם SIGSEGV
(קוד SEGV_MTESERR
) ומידע מלא על הגישה לזיכרון והכתובת התקלה.
אנו ממליצים להשתמש במצב זה במהלך בדיקה כחלופה ל-HWASan/KASAN או בייצור כאשר תהליך היעד מייצג משטח התקפה פגיע. בנוסף, כאשר מצב ASYNC הצביע על נוכחות של באג, ניתן לקבל דוח באג מדויק על ידי שימוש בממשקי ה-API של זמן הריצה כדי להעביר את הביצוע למצב SYNC.
כאשר פועל במצב SYNC, מקצה ה-Android מתעד עקבות מחסנית עבור כל ההקצאות וההקצאות ומשתמש בהן כדי לספק דוחות שגיאה טובים יותר הכוללים הסבר על שגיאת זיכרון, כגון שימוש-לאחר-חופשי, או גלישת מאגר, והמחסנית. עקבות של אירועי הזיכרון הרלוונטיים. דוחות כאלה מספקים מידע הקשרי יותר ומקלים על מעקב ותיקון באגים.
מצב אסינכרוני (ASYNC)
מצב זה מותאם לביצועים מעל לדיוק של דוחות באגים וניתן להשתמש בו כזיהוי תקורה נמוכה עבור באגי בטיחות בזיכרון.
במקרה של חוסר התאמה של תג, המעבד ממשיך בביצוע עד לכניסת הליבה הקרובה ביותר (לדוגמה, פסיקת מערכת הפעלה או טיימר), שם הוא מסיים את התהליך עם SIGSEGV
(קוד SEGV_MTEAERR
) מבלי להקליט את הכתובת הפגומה או את הגישה לזיכרון.
אנו ממליצים להשתמש במצב זה בייצור על בסיסי קוד שנבדקו היטב שבהם ידוע כי צפיפות באגי הבטיחות בזיכרון נמוכה, דבר אשר מושג על ידי שימוש במצב SYNC במהלך הבדיקה.
מצב א-סימטרי (ASYMM)
תכונה נוספת ב-Arm v8.7-A, מצב MTE Asymmetric מספק בדיקה סינכרונית של קריאות זיכרון, ובדיקה אסינכרונית של כתיבה בזיכרון, עם ביצועים דומים לאלו של מצב ASYNC. ברוב המצבים, מצב זה מהווה שיפור לעומת מצב ASYNC, ואנו ממליצים להשתמש בו במקום ASYNC בכל פעם שהוא זמין.
מסיבה זו, אף אחד מממשקי ה-API המתוארים להלן לא מזכיר את המצב האסימטרי. במקום זאת, ניתן להגדיר את מערכת ההפעלה כך שתשתמש תמיד במצב אסימטרי כאשר מתבקש אסינכרוני. אנא עיין בסעיף "הגדרת רמת ה-MTE המועדפת הספציפית למעבד" לקבלת מידע נוסף.
MTE במרחב המשתמש
הסעיפים הבאים מתארים כיצד ניתן להפעיל MTE עבור תהליכי מערכת ויישומים. MTE מושבת כברירת מחדל, אלא אם אחת מהאפשרויות שלהלן מוגדרת עבור תהליך מסוים (ראה אילו רכיבים MTE מופעל להלן ).
הפעלת MTE באמצעות מערכת הבנייה
כמאפיין כלל תהליך, MTE נשלט על ידי הגדרת זמן הבנייה של קובץ ההפעלה הראשי. האפשרויות הבאות מאפשרות לשנות הגדרה זו עבור קובצי הפעלה בודדים, או עבור ספריות משנה שלמות בעץ המקור. מתעלמים מההגדרה בספריות, או בכל יעד שאינו בר הפעלה או בדיקה.
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 באמצעות מאפייני מערכת
ניתן לעקוף את הגדרות הבנייה לעיל בזמן ריצה על ידי הגדרת מאפיין המערכת הבא:
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 באמצעות משתנה סביבה
דרך נוספת לעקוף את הגדרת הבנייה היא על ידי הגדרת משתנה הסביבה: MEMTAG_OPTIONS=(off|sync|async)
אם מוגדרים גם משתנה הסביבה וגם מאפיין המערכת, המשתנה מקבל עדיפות.
הפעלת MTE עבור יישומים
אם לא צוין, MTE מושבת כברירת מחדל, אך אפליקציות שרוצות להשתמש ב-MTE יכולות לעשות זאת על ידי הגדרת android:memtagMode
תחת התג <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 בכל הקבצים הבינאריים המקוריים במהלך הפיתוח וההעלאה. זה עוזר לזהות באגי בטיחות בזיכרון מוקדם ומספק כיסוי משתמש ריאלי, אם מופעל בבדיקות בנייה.
אנו ממליצים בחום להפעיל MTE במצב סינכרוני בכל הקבצים הבינאריים המקוריים במהלך הפיתוח
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
כמו בכל משתנה במערכת הבנייה, SANITIZE_TARGET
יכול לשמש כמשתנה סביבה או כהגדרת make
(לדוגמה, בקובץ product.mk
).
שים לב שזה מאפשר MTE עבור כל התהליכים המקוריים, אך לא עבור יישומים (שמחולקים מ- zygote64
) שעבורם ניתן להפעיל MTE לפי ההוראות למעלה .
הגדרת רמת ה-MTE המועדפת הספציפית למעבד
בחלק מהמעבדים הביצועים של MTE במצבי ASYMM או אפילו SYNC עשויים להיות דומים לאלו של ASYNC. זה עושה את זה כדאי לאפשר בדיקות מחמירות יותר על אותם מעבדים כאשר מתבקש מצב בדיקה פחות קפדני, על מנת לזכות ביתרונות זיהוי השגיאות של הבדיקות המחמירות ללא חסרונות הביצועים.
כברירת מחדל, תהליכים שהוגדרו לפעול במצב ASYNC יפעלו במצב ASYNC בכל המעבדים. כדי להגדיר את הליבה להריץ תהליכים אלה במצב SYNC במעבדים ספציפיים, יש לכתוב את סינכרון הערך לערך sysfs
/sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
בזמן האתחול. ניתן לעשות זאת עם סקריפט init. לדוגמה, כדי להגדיר מעבדים 0-1 להפעיל תהליכים במצב ASYNC במצב SYNC, ומעבדים 2-3 לשימוש בהפעלה במצב ASYMM, ניתן להוסיף את הדברים הבאים לסעיף init של סקריפט init של הספק:
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 יכילו עקבות מחסנית מדויקת של מיקום שגיאת הזיכרון. עם זאת, הם לא יכללו מעקב אחר הקצאה או מחסנית. עקבות מחסנית אלו זמינות רק אם התהליך מוגדר לפעול במצב 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
- מאפשר זיהוי דטרמיניסטי של באגים ליניאריים של גלישת מאגר ו-underflow על ידי הקצאת ערכי תג ברורים להקצאות סמוכות. למצב זה יש סיכוי מופחת מעט לזהות באגים לאחר שימוש ללא שימוש, מכיוון שרק מחצית מערכי התג האפשריים זמינים עבור כל מיקום זיכרון. אנא זכור ש-MTE אינו יכול לזהות גלישה בתוך אותו גרגיר תג (נתח מיושר של 16 בתים), ויכול לפספס הצפות קטנות אפילו במצב זה. הצפה כזו לא יכולה להיות הגורם לשחיתות בזיכרון, מכיוון שהזיכרון בתוך גרגיר אחד לעולם אינו משמש להקצאות מרובות. -
M_MEMTAG_TUNING_UAF
- מאפשר תגיות אקראית באופן עצמאי עבור הסתברות אחידה של ~93% לזיהוי באגים מרחביים (הצפת מאגר) וזמניים (שימוש לאחר חינמי).
בנוסף לממשקי ה-API שתוארו לעיל, משתמשים מנוסים עשויים לרצות להיות מודעים לנקודות הבאות:
- הגדרת אוגר החומרה
PSTATE.TCO
יכולה לדכא זמנית את בדיקת התגים ( דוגמה ). לדוגמה, בעת העתקת טווח זיכרון עם תוכן תג לא ידוע, או טיפול בצוואר בקבוק ביצועים בלולאה חמה. - בעת שימוש
M_HEAP_TAGGING_LEVEL_SYNC
, מטפל התרסקות המערכת מספק מידע נוסף כגון מעקבי הקצאה וביטול הקצאה. פונקציונליות זו דורשת גישה לסיביות התג והיא מופעלת על ידי העברת הדגלSA_EXPOSE_TAGBITS
בעת הגדרת המטפל באותות. כל תוכנית שמגדירה את מטפל האותות שלה ומאצילה קריסות לא ידועות למערכת אחת, מומלץ לעשות את אותו הדבר.
MTE בקרנל
כדי להפעיל את 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
). ללא קשר לאפשרות זו, בדיקת התגים מושבתת לאחר השגיאה הראשונה שדווחה.
שימוש מומלץ
אנו ממליצים בחום להשתמש במצב SYNC במהלך העלאה, פיתוח ובדיקה. אפשרות זו צריכה להיות מופעלת באופן גלובלי עבור כל התהליכים המשתמשים במשתנה הסביבה או עם מערכת הבנייה . במצב זה, באגים מתגלים בשלב מוקדם בתהליך הפיתוח, בסיס הקוד מתייצב מהר יותר והעלות של זיהוי באגים בהמשך הייצור נמנעת.
אנו ממליצים בחום להשתמש במצב ASYNC בייצור. זה מספק כלי תקורה נמוך לזיהוי נוכחותם של באגי בטיחות בזיכרון בתהליך, כמו גם הגנה מעמיקה נוספת. לאחר זיהוי באג, המפתח יכול למנף את ממשקי ה-API של זמן הריצה כדי לעבור למצב SYNC ולקבל עקבות מחסנית מדויקת מקבוצת משתמשים שנדגמה.
אנו ממליצים בחום להגדיר את רמת ה-MTE המועדפת הספציפית למעבד עבור ה-SoC. למצב Asymm יש בדרך כלל את אותם מאפייני ביצועים כמו ASYNC, והוא כמעט תמיד עדיף על פניו. ליבות קטנות לפי הסדר מציגות לעתים קרובות ביצועים דומים בכל שלושת המצבים, וניתן להגדיר אותן כך שיעדיפו SYNC.
מפתחים צריכים לבדוק את נוכחותן של קריסות על ידי בדיקת /data/tombstones
, logcat
או על ידי ניטור צינור הספק DropboxManager
לאיתור באגים של משתמשי קצה. למידע נוסף על איתור באגים בקוד מקורי של Android, עיין במידע כאן .
רכיבי פלטפורמה התומכים ב-MTE
באנדרואיד 12, מספר רכיבי מערכת קריטיים לאבטחה משתמשים ב-MTE ASYNC כדי לזהות קריסות של משתמשי קצה ולפעול כשכבה נוספת של הגנה מעמיקה. רכיבים אלו הם:
- דמוני רשת וכלי עזר (למעט
netd
) - Bluetooth, SecureElement, NFC HALs ויישומי מערכת
-
statsd
daemon -
system_server
-
zygote64
(כדי לאפשר ליישומים להצטרף לשימוש ב-MTE)
יעדים אלו נבחרו על סמך הקריטריונים הבאים:
- תהליך מיוחס (מוגדר כתהליך שיש לו גישה למשהו שאין לדומיין unprivileged_app SELinux)
- מעבד קלט לא אמין ( כלל שני )
- האטה קבילה בביצועים (האטה לא יוצרת זמן אחזור גלוי למשתמש)
אנו מעודדים את הספקים לאפשר MTE בייצור עבור רכיבים נוספים, בהתאם לקריטריונים שהוזכרו לעיל. במהלך הפיתוח אנו ממליצים לבדוק את הרכיבים הללו באמצעות מצב SYNC, כדי לזהות באגים המתוקנים בקלות, ולהעריך את ההשפעה של ASYNC על הביצועים שלהם.
בעתיד, אנדרואיד מתכננת להרחיב את רשימת רכיבי המערכת שבהם MTE מופעל, בהנחיית מאפייני הביצועים של עיצובי החומרה הקרובים.