דימון (daemon) של Android Live-lock (llkd)

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

תרחישי זיהוי

ב-llkd יש שני תרחישי זיהוי: מצב D או Z קבוע, ומצב קבוע חתימה בסטאק.

מצב D או Z קבוע

אם שרשור נמצא במצב D (שינה ללא הפרעות) או Z (זומבי) ללא העברה התקדמות של יותר מ-ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms, הפקודה llkd מסתיימת בתהליך (או בתהליך הורה). אם בסריקה לאחר מכן מופיע אותו תהליך עדיין קיים, ה-llkd מאשר תנאי של נעילה בזמן אמת מבהיל את הליבה באופן שיספק את דוח איתור הבאגים המפורט ביותר תנאי.

llkd כולל טיימר מפקח (watchdog) ששולח התראות אם llkd ננעל. הטיימר המפקח (watchdog) הוא להכפיל את משך הזמן הצפוי לזרימה דרך הלולאה הראשית, וכל דגימה ro.llk_sample_ms.

חתימת מקבץ מתמיד

בגרסאות של ניפוי באגים למשתמשים, llkd יכול לזהות נעילות חיים של ליבה (kernel) באמצעות שימוש קבוע בדיקת חתימת סטאק. אם בשרשור בכל מצב חוץ מ-Z יש ערך קבוע מופיע סמל הליבה ro.llk.stack שדווח במשך זמן רב מ- ro.llk.timeout_ms או ro.llk.stack.timeout_ms, האפשרות llkd תפסיק את התהליך (גם אם יש התקדמות בתזמון מראש). אם בסריקה לאחר מכן מופיע אותו תהליך עדיין קיים, ה-llkd מאשר תנאי של נעילה בזמן אמת מבהיל את הליבה באופן שיספק את דוח איתור הבאגים המפורט ביותר תנאי.

הבדיקה של lldk נמשכת באופן רציף כשקיים תנאי נעילה בזמן אמת מחפש את המחרוזות המורכבות symbol+0x או symbol.cfi+0x קובץ /proc/pid/stack ב-Linux. רשימת הסמלים נמצאת בro.llk.stack ו ברירת המחדל היא רשימה המופרדת בפסיקים של cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable.

הסמלים צריכים להיות נדירים וקצרים מספיק, כדי שבמערכת טיפוסית מופיעה רק פעם אחת בדגימה במהלך פרק הזמן הקצוב לתפוגה של ro.llk.stack.timeout_ms (הדגימות מתבצעות בכל ro.llk.check_ms). עקב מחסור של הגנת ABA, זו הדרך היחידה למנוע הפעלה כוזבת. הסמל הפונקציה חייבת להופיע מתחת לפונקציה שקוראת לנעילה שיכולה להכיל נתונים. אם המיקום המנעול נמצא מתחת או בפונקציית הסמל, הסמל מופיע בכל תהליכים, ולא רק את זה שגרם לנעילה.

כיסוי

הטמעת ברירת המחדל של llkd לא עוקבת אחרי init, [kthreadd] או [kthreadd] יוצאים. כדי ש-llkd יכסה שרשורים עם [kthreadd] רשתות:

  • אסור לנהגים להישאר במצב D קבוע,

או

  • לנהגים צריכים להיות מנגנונים לשחזור השרשור אם מבטלים אותו באופן חיצוני. לדוגמה, משתמשים ב-wait_event_interruptible() במקום ב- wait_event().

אם אחד מהתנאים שלמעלה מתקיים, אפשר לשנות את רשימת הישויות שנחסמו ל-llkd אל כולל רכיבי הליבה. בדיקת סמלי הערימה כרוכה בתהליך נוסף רשימת ישויות שנחסמו למניעת הפרות מדיניות בשירותים שחוסמים את ptrace ב-AI.

נכסי Android

llkd מגיב לכמה נכסי Android (מפורטים בהמשך).

  • נכסים בשם prop_ms נמצאים באלפיות שנייה.
  • מאפיינים שבהם נעשה שימוש בפסיק (,) לרשימות, תו מפריד מוביל כדי לשמור את ערך ברירת המחדל, ואז להוסיף או להחסיר רשומות עם סימן פלוס אופציונלי תחיליות (+) ומינוס (-) בהתאמה. ברשימות האלה, המחרוזת false מקביל לרשימה ריקה, וערכים ריקים או חסרים מפנים את ערך ברירת המחדל שצוין.

ro.config.low_ram

המכשיר מוגדר עם זיכרון מוגבל.

Ro.debuggable

המכשיר מוגדר לניפוי באגים אצל משתמשים או ל-build מחדש.

ro.llk.sysrq_t

אם המאפיין הוא eng, ברירת המחדל היא לא ro.config.low_ram או ro.debuggable. אם הערך שלו הוא true, ביטול כל השרשורים (sysrq t).

ro.llk.enable

אישור להפעלת דימון (daemon) של נעילה בשידור חי. ברירת המחדל היא false.

llk.enable

בוצעה הערכה לפיתוח גרסאות build לעידוד השימוש באפליקציה. ברירת המחדל היא ro.llk.enable.

ro.kungtask.enable

אישור להפעלת הדימון (daemon) של [khungtask]. ברירת המחדל היא false.

kungtask.enable

בוצעה הערכה לפיתוח גרסאות build לעידוד השימוש באפליקציה. ברירת המחדל היא ro.khungtask.enable.

ro.llk.mlockall

הפעלת השיחה אל mlockall(). ברירת המחדל היא false.

ro.khutask.timeout

מגבלת זמן של [khungtask] לכל היותר. ברירת המחדל היא 12 דקות.

ro.llk.timeout_ms

מגבלת זמן מקסימלית D או Z. ברירת המחדל היא 10 דקות. צריך להכפיל את הערך הזה כדי להגדיר את טיימר מפקח (watchdog) של llkd.

ro.llk.D.timeout_ms

מגבלת זמן מקסימלית D. ברירת המחדל היא ro.llk.timeout_ms.

ro.llk.Z.timeout_ms

מגבלת זמן מקסימלית של Z. ברירת המחדל היא ro.llk.timeout_ms.

ro.llk.stack.timeout_ms

בדיקת מגבלת הזמן המקסימלית של סמלי ערימה קבועים. ברירת המחדל היא ro.llk.timeout_ms פעיל רק ב-userdebug או בגרסאות build של יצירת עניין.

ro.llk.check_ms

דגימות של שרשורים ל-D או Z. ברירת המחדל היא שתי דקות.

ro.llk.stack

חיפוש סמלים של סטאק ליבה (kernel), שאם הם קיימים באופן קבוע מציינים מערכת המשנה נעולה. ברירת המחדל היא cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable רשימה של סמלי ליבה (kernel) שמופרדים בפסיקים. הבדיקה לא מאפשרת תזמון העברה ABA, למעט באמצעות סקרים בכל ro.llk_check_ms במהלך התקופה ro.llk.stack.timeout_ms, לכן סמלי מקבצים צריכים להיות נדירים חולף (לא סביר שסמל יופיע באופן קבוע דוגמאות של המקבץ). מחפש התאמה עבור symbol+0x או symbol.cfi+0x בהרחבת מקבץ. זמין רק ב-userdebug או in-eng builds; בעיות אבטחה בגרסאות build של משתמשים גורמות להרשאות מוגבלות למנוע את הבדיקה הזו.

ro.llk.black.process

llkd לא עוקב אחרי התהליכים שצוינו. ברירת המחדל היא 0,1,2 (kernel, init ו-[kthreadd]) וגם שמות של תהליכים init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1] תהליך יכול להיות הפניה של comm, cmdline או pid. ברירת מחדל אוטומטית יכול להיות גדול מגודל הנכס המקסימלי הנוכחי של 92.

ro.llk.black.parent

llkd לא עוקב אחרי תהליכים שיש להם את תבניות ההורה שצוינו. ברירת מחדל היא 0,2,adbd&[setsid] (kernel, [kthreadd], ו-adbd רק לזומבים setsid). מפריד אמפרסנד (&) מציין שהמערכת מתעלמת רק מההורה בשילוב עם תהליך הצאצא של היעד. אמפרסנד נבחר מפני שהוא הוא אף פעם לא חלק משם תהליך. אבל setprop במעטפת דורש כדי לסמן את האמפרסנד בתו בריחה (escape) או לצטט אותן, למרות שקובץ init rc שבו בדרך כלל, אין את הבעיה הזו. הורה או תהליך יעד יכולים להיות הפניה אל comm, cmdline או pid.

ro.llk.black.uid

הפונקציה llkd לא עוקבת אחרי תהליכים שתואמים ל-UID שצוין. רשימה של מספרים או שמות המופרדים באמצעות פסיקים. ברירת המחדל ריקה או false.

ro.llk.black.process.stack

llkd לא עוקב אחר קבוצת המשנה שצוינה של תהליכים לסטאק תוכנות נעילה בזמן אמת חתימות. ברירת המחדל היא שמות התהליכים init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd מניעת פריצה הפרה המשויכת לתהליכים שחוסמים את ptrace (כי הם לא יכולים להיות מסומנת). פעיל רק ב-userdebug ובגרסאות build של מעורבות. פרטים על build הסוגים, קראו את המאמר יצירת Android.

חששות בנוגע לארכיטקטורה

  • המאפיינים מוגבלים ל-92 תווים (עם זאת, ברירת המחדל מתעלמת מהמאפיין הזה שהוגדר בקובץ include/llkd.h במקורות).
  • הדימון המובנה של [khungtask] הוא כללי מדי והחיפוש שלו מתבצע באמצעות קוד הנהג נמצאת במצב D יותר מדי. המעבר ל-S יגרום לכך שהמשימות יימחקו (וניתן להחזיר אותם במקרה הצורך על ידי הנהגים).

ממשק הספרייה (אופציונלי)

אפשר לשלב את llkd בדימון (daemon) אחר בעל הרשאות באמצעות ממשק C הבא מהרכיב libllkd:

#include "llkd.h"
bool llkInit(const char* threadname) /* return true if enabled */
unsigned llkCheckMillseconds(void)   /* ms to sleep for next check */

אם מציינים שם שרשור, השרשור מתחיל באופן אוטומטי. אחרת, מבצע הקריאה החוזרת חייב לקרוא לפונקציה llkCheckMilliseconds בלולאה הראשית. הפונקציה מחזירה את הערך פרק זמן לפני הקריאה הצפויה הבאה ל-handler הזה.