Демон Android Live-LocK (llkd)

Android 10 включает в себя демон Android Live-LocK ( llkd ), который предназначен для обнаружения и устранения взаимоблокировок ядра. Компонент llkd предоставляет автономную реализацию по умолчанию, но вы также можете интегрировать код llkd в другую службу либо как часть основного цикла, либо как отдельный поток.

Сценарии обнаружения

У llkd есть два сценария обнаружения: постоянное состояние D или Z и постоянная сигнатура стека.

Постоянное состояние D или Z

Если поток находится в состоянии D (непрерывный сон) или Z (зомби) без прогресса вперед дольше, чем ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms , llkd убивает процесс (или родительский процесс). ). Если последующее сканирование показывает, что тот же процесс продолжает существовать, llkd подтверждает состояние активной блокировки и паникует ядро ​​таким образом, чтобы предоставить наиболее подробный отчет об ошибке для этого состояния.

llkd включает в себя систему самоконтроля, которая подает сигнал тревоги, если llkd блокируется; сторожевой таймер в два раза превышает ожидаемое время прохождения через основной цикл, а выборка производится каждый ro.llk_sample_ms .

Постоянная подпись стека

Для выпусков пользовательской отладки llkd может обнаруживать живые блокировки ядра, используя постоянную проверку подписи стека. Если поток в любом состоянии, кроме 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 .

Свойства Android

llkd реагирует на несколько свойств Android (перечисленных ниже).

  • Свойства с именем prop_ms указаны в миллисекундах.
  • Свойства, в которых для списков используется разделитель-запятая (,), используют ведущий разделитель, чтобы сохранить запись по умолчанию, а затем добавляют или вычитают записи с необязательными префиксами плюс (+) и минус (-) соответственно. Для этих списков строка «false» является синонимом пустого списка, а пустые или отсутствующие записи используют указанное значение по умолчанию.

ro.config.low_ram

Устройство настроено с ограниченным объемом памяти.

ro.debuggable

Устройство настроено для пользовательской отладки или сборки на английском языке.

ro.llk.sysrq_t

Если свойство имеет значение «eng», значением по умолчанию является не ro.config.low_ram или ro.debuggable . Если это правда, выгрузить все потоки ( sysrq t ).

ro.llk.enable

Разрешить включение демона live-lock. По умолчанию — ложь.

llk.enable

Оценивается для англоязычных сборок. По умолчанию — ro.llk.enable .

ro.khungtask.enable

Разрешите включение демона [khungtask] . По умолчанию — ложь.

khungtask.enable

Оценивается для англоязычных сборок. По умолчанию — ro.khungtask.enable .

ro.llk.mlockall

Включите вызов mlockall() . По умолчанию — ложь.

ro.khungtask.timeout

[khungtask] максимальный срок. По умолчанию — 12 минут.

ro.llk.timeout_ms

Максимальный срок D или Z. По умолчанию — 10 минут. Удвойте это значение, чтобы установить сторожевой таймер для 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 или eng .

ro.llk.check_ms

Примеры тем для D или Z. По умолчанию — две минуты.

ro.llk.stack

Проверяет символы стека ядра, которые, если они постоянно присутствуют, могут указывать на то, что подсистема заблокирована. По умолчанию — cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable список символов ядра, разделенных запятыми. Проверка не выполняет ABA прямого планирования, за исключением опроса каждого ro.llk_check_ms за период ro.llk.stack.timeout_ms , поэтому символы стека должны быть исключительно редкими и мимолетными (очень маловероятно, чтобы символ постоянно появлялся во всех образцы стека). Проверяет совпадение " symbol+0x" или " symbol.cfi+0x" в расширении стека. Доступно только в сборках userdebug или eng ; проблемы безопасности в пользовательских сборках приводят к ограничению привилегий, которые препятствуют этой проверке.

ro.llk.blacklist.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.blacklist.parent

llkd не отслеживает процессы, имеющие указанных родителей. По умолчанию — 0,2,adbd&[setsid] ( kernel , [kthreadd] и adbd только для зомби- setsid ). Разделитель амперсанд (&) указывает, что родительский процесс игнорируется только в сочетании с целевым дочерним процессом. Был выбран амперсанд, поскольку он никогда не является частью имени процесса; однако setprop в оболочке требует, чтобы амперсанд был экранирован или заключен в кавычки, хотя файл init rc , где это обычно указывается, не имеет этой проблемы. Родительский или целевой процесс может быть ссылкой comm , cmdline или pid .

ro.llk.blacklist.uid

llkd не отслеживает процессы, соответствующие указанным uid(ам). Список номеров или имен uid, разделенных запятыми. По умолчанию пусто или ложно.

ro.llk.blacklist.process.stack

llkd не отслеживает указанное подмножество процессов на наличие сигнатур стека действующих блокировок. По умолчанию используются имена процессов init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd . Предотвращает нарушение политики безопасности, связанное с процессами, блокирующими ptrace (поскольку их невозможно проверить). Активен только в сборках userdebug и eng . Подробную информацию о типах сборки см. в разделе Сборка Android .

Архитектурные проблемы

  • Свойства ограничены 92 символами (однако это игнорируется для значений по умолчанию, определенных в файле include/llkd.h в исходных текстах).
  • Встроенный демон [khungtask] слишком общий и срабатывает на коде драйвера, который слишком долго находится в состоянии D. Переключение на S сделает задачу(и) удаляемыми (и при необходимости восстанавливаемыми драйверами).

Интерфейс библиотеки (опционально)

При желании вы можете включить llkd в другой привилегированный демон, используя следующий интерфейс C из компонента libllkd :

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

Если указано имя потока, поток создается автоматически, в противном случае вызывающая сторона должна вызвать llkCheckMilliseconds в своем основном цикле. Функция возвращает период времени до следующего ожидаемого вызова этого обработчика.